☰
SQLi-Labs靶场搭建实战:PHP版本、数据库初始化与启动排错
2026/9/29 2:08:50 网站建设 项目流程

很多人学 Web 安全的第一个门槛不是注入思路,而是靶场根本跑不起来。DVWA 点两下就能进,Pikachu 装完就能打,可轮到 SQLi-Labs,一堆人卡在"页面白屏""数据库连接失败""Call to undefined function mysql_connect"这几行报错上,折腾一整晚最后放弃。SQLi-Labs 这套靶场本身不复杂,它是 Audi-1 开源的一套 PHP + MySQL 注入练习平台,从 Less-1 到 Less-65,几乎把联合查询、报错注入、布尔盲注、时间盲注、堆叠查询、POST 型、Cookie 型、HTTP 头注入这些经典场景都覆盖了一遍,是练手 SQL 注入最系统的一套环境。但它有个"老"的毛病——代码年代久远,对 PHP 版本和数据库配置相当挑剔。这篇就把搭建过程从环境选型、源码落位、数据库初始化到启动失败排查,完整走一遍,适合刚入门想搭本地环境的新手,也适合装了半天没装上的朋友对症下药。

1. 搭之前先搞清楚:SQLi-Labs 到底解决什么问题

1.1 它和 DVWA、Pikachu 的定位差异

同样叫"靶场",DVWA、Pikachu、SQLi-Labs 的定位其实完全不同。DVWA 是综合性靶场,一个功能模块对应多种漏洞类型,适合建立"漏洞全景"的认知;Pikachu 偏教学化,每个漏洞都配了说明和提示,适合当教材对照着做;而 SQLi-Labs 是垂直深度型,它把"SQL 注入"这一件事拆成了 65 个关卡,每一关考察的注入点和防护思路都不一样。

这个差异直接决定了你搭建时的心态。DVWA 装不上,你换个整合包就完事了;SQLi-Labs 装不上,你没法用别的靶场替代它的练习量——因为它是目前单点覆盖最全的注入练习环境。所以搭建这件事值得认真对待,不要用"随便找个一键包糊上去"的思路去糊它,后面关卡里一旦涉及字符集、HTTP 头、编码转换这些细节,环境本来就不干净会让你分不清是靶场问题还是自己思路问题。

另外提一句,网上还有不少同类环境可以搭配使用:DVWA 适合练完综合漏洞后回头做对比,Vulnstack 和红日系列偏内网渗透场景,vulhub 偏容器化的漏洞复现。它们和 SQLi-Labs 不是替代关系,是不同阶段的配套。你现在的目标很明确——先把这套注入练习环境稳稳跑起来。

1.2 学习路径上的实际作用

我见过不少人问"sqli-labs 通关有没有意义"。说实话,单纯背 payload 通关没意义,但把它当作"手写注入语句的肌肉记忆训练场"就很有价值。第一到第十关让你把联合查询的字段数判断、回显位确定、库表字段枚举这一整条链路走顺;后面的盲注关卡逼着你用二分法和条件判断去猜数据,这种"没有回显还得拿数据"的思维,在真实测试场景里出现频率极高。

还有一点常被忽略:SQLi-Labs 是练工具配合的好地方。手工通了之后,再拿 Burp Suite 去抓包改包、挂字典批量跑,你会明显感受到工具替你省下了哪些重复劳动。但前提还是那句——环境得先干净、稳定、能复现,否则你连"这一关到底是盲注还是报错注入"都没法稳定验证。

1.3 搭建前必须有的心理准备

SQLi-Labs 的源码最后一个大版本更新已经是很久以前的事了,那个年代的 PHP 还在用mysql_*系列函数,而这类函数在 PHP 7.0 就被标记废弃、PHP 7.0 之后正式移除。所以你会看到很多教程让你装 PHP 5.6 或者 PHP 7.0,这不是他们偷懒,是这套代码真的跑不了高版本。同时它对magic_quotes_gpc这个早已被移除的配置项有依赖,某些关卡的行为会受它影响。

理解了这一点,你就明白搭建的核心矛盾在哪:现代环境默认的 PHP 版本太高,而靶场代码太老。解决办法无非两条路——要么把环境降到靶场能接受的版本,要么给靶场代码打补丁。前者省事但影响你本机其它项目,后者干净但需要动源码。下面会分别说清楚。

2. 环境选型:三条部署路线怎么选

2.1 三种路线的取舍对比

搭建 SQLi-Labs,本质上只需要三样东西:Web 服务器(Apache 或 Nginx)、PHP 运行环境、MySQL 数据库。区别只在于你怎么把它们凑齐。目前主流是三条路:

路线代表方案优点缺点适合谁
集成面板phpstudy、XAMPP、宝塔面板版本可切换、图形化、装完即用面板本身占资源、配置被封装不易理解新手首选
手工编译/包管理Linux 下 apt 装 LAMP环境透明、可控性最高配置项多、排错要懂原理想搞懂底层的人
容器化Docker 拉现成镜像秒级部署、环境隔离、可反复重建需要懂 Docker 基本操作有容器基础的人

新手我一般直接推荐集成面板,尤其是 phpstudy,因为它能一键切换 PHP 版本,正好解决上面说的"PHP 版本太高"这个死结。你不需要懂 Apache 的虚拟主机配置,也不需要手动去改 php.ini,点几下就能把环境调成靶场能接受的版本。

2.2 PHP 版本是这套靶场的生死线

这是整个搭建过程里最关键的一个决策点,单独拎出来讲。SQLi-Labs 官方代码依赖mysql_connect()、mysql_query()、mysql_error()这一系列函数,理论上最舒服的版本是PHP 5.4 到 5.6。如果你装的是 PHP 7.4 或者 PHP 8.x,访问首页就会出现致命错误,页面直接白掉或抛出未定义函数的异常。

所以在集成面板里,第一件事就是把 PHP 版本切到 5.6。操作路径通常是:打开面板的"软件管理"或"网站"页面,找到 PHP 版本切换选项,选中 5.6 并应用。切换之后重启一下 Web 服务。

如果你因为其它项目必须留在 PHP 8 环境,那就得走打补丁这条路。核心思路是把所有mysql_*函数替换成mysqli_*或者 PDO 的等价写法。这个工作量不小,因为涉及sql-connections目录下多个文件以及每一关的入口文件。社区里有人做过兼容补丁,思路基本是:

// 原始写法(PHP 5 可用,PHP 7+ 报错) $con = mysql_connect($host, $user, $pass); // 兼容写法 $con = mysqli_connect($host, $user, $pass, $dbname);

提示:如果你只是单纯想学注入,别跟自己较劲去改源码。装个 PHP 5.6 的独立环境,成本远低于改造整个靶场。改源码看似干净,实际容易出现"某几关行为不一致"的隐性坑。

2.3 Docker 路线的具体操作

如果你本机已经有 Docker,这条路线其实最省心,因为它把 PHP 版本、MySQL、Apache 全部打包好了,不用你操心依赖。常见做法是拉取社区维护的 SQLi-Labs 镜像,然后把 80 端口映射出来:

# 拉取镜像 docker pull acgpiano/sqli-labs # 启动容器,映射到本机 8888 端口,避免和已有服务冲突 docker run -dt --name sqli-labs -p 8888:80 acgpiano/sqli-labs # 查看容器状态 docker ps

启动后浏览器访问http://localhost:8888就能看到靶场首页。容器的好处是,就算你把数据库搞乱了,删掉容器重新docker run一遍,几十秒就能回到干净状态。练盲注类关卡时数据库会被你反复写脏,这个"一键重置"能力很实用。

但 Docker 路线也有个小坑:容器内 MySQL 的认证方式、字符集可能和手工搭建的不一样,某些依赖特定字符集的关卡表现会有差异。如果你追求和教程完全一致的行为,还是集成面板更稳妥。

3. 源码落位与配置文件的对应关系

3.1 目录结构先看明白再动手

源码从代码托管平台搜索 SQLi-Labs 就能找到,作者是 Audi-1。下载解压后,先别急着往 Web 根目录扔,花两分钟看下目录结构,后面排错能省一半时间。核心目录大致是这样:

  • 根目录下是Less-1、Less-2一直到Less-65的文件夹,每一关是独立入口,这是它和 DVWA 最大的结构差异。
  • sql-connections目录是整套系统的"公共心脏",数据库连接参数、建表建库脚本、公共函数都在这。
  • index.html和index.php是首页导航,里面有初始化数据库的按钮。

理解这一点很重要:靶场不是"一个站点 + 多个路由",而是"几十个独立小站点共享一套数据库配置"。所以数据库连不上,是全部 65 关同时挂掉;如果只有个别关卡报错,那问题多半出在那一关的代码上,跟环境无关。这个判断能帮你快速缩小排查范围。

3.2 db-creds.inc 与数据库账号的对应关系

这是搭建过程中最容易出错的地方。sql-connections目录下有个db-creds.inc文件,整套靶场靠它连接数据库,内容大致是这几行:

<?php $host = "localhost"; $dbuser = "root"; $dbpass = "root"; $dbname = "security"; ?>

你需要做的,就是让这里的$dbuser和$dbpass与你本机 MySQL 的实际账号密码完全一致。很多人装完之后页面能开但初始化报错,八成就是这里没对上——比如面板里 MySQL 密码是root123,文件里还是默认的root。

我的建议是,别去改 MySQL 的密码来迁就文件,直接改文件更安全。因为改数据库密码可能影响本机其它项目,而改一个靶场专用的配置文件,风险为零。改完之后保存,别漏了分号和引号,PHP 配置文件语法错误会导致整个连接逻辑失效。

另外$dbname这一项要留意,不同版本源码里的默认库名可能不一样,有的是security,有的是security加别的后缀。这个值不是随便填的,它必须和建库脚本里创建的库名对应。稳妥的做法是先打开sql-connections里的建库 SQL 文件看一眼库名,再回头核对配置文件。

3.3 站点根目录与访问路径设置

把解压出来的整个 SQLi-Labs 文件夹放到 Web 服务器的站点根目录下。集成面板的话,站点根目录通常是面板安装目录下的WWW或wwwroot;Apache 手工装的话一般是/var/www/html。

放好之后,访问地址就取决于你的端口和目录名。假设面板用的 80 端口,文件夹名叫sqli-labs,那么地址就是http://localhost/sqli-labs/。如果访问后是目录列表而不是靶场首页,说明默认文档顺序有问题,检查一下目录下是否有index.php且服务器允许它作为默认页。

还有一种情况是首页能开,但点任何一个 Less 都跳转到http://localhost/Less-1/这种丢了子目录的地址。这通常是源码里用了相对根路径的跳转,解决办法是把文件夹直接放在站点根目录下、不要嵌套额外层级,或者配置一个指向该文件夹的虚拟主机,让它的根路径就是靶场目录。

4. 数据库初始化:setup-db 在背后做了什么

4.1 正确的首次访问顺序

SQLi-Labs 第一次访问不能直接点关卡,得先初始化数据库。打开首页index.php,页面顶部通常会有一个入口,写着类似 "Setup/reset Database for labs" 的链接,点击它会执行sql-connections下的初始化脚本。

这个脚本干的事情其实很明确:创建security数据库,建好几张表(users、emails、uagents等),然后把预设的测试数据插进去。你后面所有关卡的注入目标,都是往这几张表里查数据。所以这一步没跑成功,后面 65 关全部是空的,查询会返回"表不存在"。

点击之后如果看到一排绿色的成功提示,说明环境通了。如果看到红色的报错,先别慌,报错信息本身就是最好的线索,下一节专门讲怎么读这些报错。

4.2 手动导入的兜底做法

有时候图形化初始化会因为权限或字符集问题失败,这时候可以走命令行兜底。思路是直接用数据库客户端连上去,手动执行建库脚本:

# 登录 MySQL mysql -u root -p # 创建数据库并指定字符集 CREATE DATABASE security DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci; # 切换数据库 USE security; # 导入建表脚本 source /path/to/sqli-labs/sql-connections/db-schema.sql;

这种方式的好处是完全绕开了 PHP 层的报错,能让你确认"到底是数据库权限问题,还是 PHP 连接问题"。如果手动导入成功、图形化初始化失败,那问题就在 PHP 连接配置上;如果手动导入也失败,那就是数据库账号或者脚本本身的问题。这个二分法在实际排错里非常好用。

手动导入完,还要记得把配置文件里的$dbname核对一遍,确保指向security。有些版本的建库脚本会用不同的库名,别想当然。

5. 启动失败排查:一条从表象到根因的链路

5.1 页面白屏但 HTTP 返回 200

这是最让人头大的一类问题——浏览器一片空白,但看网络面板状态码是 200,说明请求成功、服务正常,问题出在 PHP 执行阶段。白屏几乎必然是致命错误被display_errors关掉了。

第一步,打开php.ini,把这两个开关打开:

display_errors = On error_reporting = E_ALL

改完重启 Web 服务,再访问一次,白屏就会变成带具体文件行号的错误信息。这一步是所有排错的前提,看不到错误信息就是在盲猜。

打开之后最常见的两种报错,一种是Call to undefined function mysql_connect(),这就是前面说的 PHP 版本过高问题,回到第 2.2 节切换版本即可。另一种是Access denied for user 'root'@'localhost',这是数据库账号密码不匹配,回到 3.2 节核对配置文件。

5.2 数据库连接类报错的逐层定位

数据库连不上,可能是四层原因,从外到内依次排查:

  1. 账号密码不对。用命令行mysql -u root -p手动登录一次,确认密码正确。这一步能排除 90% 的问题。
  2. 数据库服务没启动。面板里看 MySQL 服务状态,绿色才是运行中。有时候是端口冲突导致启动失败。
  3. 权限问题。如果用的是非 root 账号,要确认它有没有security库的读写权限。图形化面板一般用 root,问题不大。
  4. 连接地址问题。配置文件里写的是localhost,如果你改成了 IP,而 MySQL 只监听本地回环,就会连不上。这种情况要么改回localhost,要么给 MySQL 开放对应主机的访问。

排查顺序千万别乱,从最简单的手动登录开始。我见过有人一上来就怀疑源码有 bug,改了半天发现是数据库密码填错了。

5.3 PHP 8 环境下的函数缺失与字符集坑

如果你铁了心要在 PHP 8 上跑,除了mysql_*函数,还要注意magic_quotes_gpc这个配置项在 PHP 8 里已经不存在了。靶场里有些代码会调用get_magic_quotes_gpc()来判断是否需要转义,这个函数在新版本里也没了,会直接报错。

处理方式是在公共函数文件里加一层兜底:

if (!function_exists('get_magic_quotes_gpc')) { function get_magic_quotes_gpc() { return false; } }

字符集也是个隐形坑。PHP 5.6 和 PHP 8 默认的连接字符集不同,某些依赖宽字节的关卡在两种环境下表现会不一样。如果你发现某个关卡的手工注入结果和教程对不上,先别怀疑自己的 payload,去确认一下连接的字符集设置。这也是我为什么反复推荐用 PHP 5.6——和绝大多数教程的环境一致,减少变量。

5.4 端口占用与访问不通

还有一类"看起来像靶场问题其实不是"的情况:端口被占。比如你本机已经跑了别的 Web 服务占了 80,面板起不来但没报明显错误。排查方式很简单,命令行看端口监听:

# Linux / macOS netstat -tlnp | grep 80 # Windows netstat -ano | findstr :80

如果 80 被占,把靶场改到 8080 或者 8888 之类不冲突的端口就行。改完记得访问地址也要跟着变。

另外就是防火墙。本地访问一般不受影响,但如果你是在虚拟机或服务器上搭,外部访问不通要先看防火墙规则放没放行对应端口。这个和靶场本身没关系,但确实卡住过不少人。

6. 环境跑通之后:Less-1 到 Less-65 怎么练才有效

6.1 关卡分组与知识对应关系

环境通了,接下来才是真正的学习。65 关不用一关一关硬啃,按类型分组练效率高得多。大致可以这样分:

关卡区间主要考察点练习重点
Less 1-10基础注入联合查询、报错注入、布尔盲注、时间盲注
Less 11-20POST 型注入表单提交、Burp 抓包改包
Less 21-38进阶场景Cookie 注入、Base64 编码、HTTP 头注入
Less 39-45堆叠查询分号多语句执行
Less 46-53Order By 注入排序参数点的利用
Less 54-65挑战关综合场景、二次注入、宽字节

按这个顺序推进,每一组内部再横向对比不同注入手法,比机械地"1、2、3"往下刷有效得多。重点不是通关数量,是每一关你都清楚"它为什么能被注入""防护为什么没拦住"。

6.2 工具配合与练习记录

手工练完前二十关之后,建议引入 Burp Suite 做抓包练习。它的价值在于把请求参数可视化,你能直接在同一个请求上做参数变形测试,而不用反复在浏览器地址栏里改 URL。对于 POST 型注入和 HTTP 头注入,抓包几乎是必用的手段。

更重要的习惯是记录。每一关我都会记三件事:注入点在哪、判断注入类型的依据是什么、最终用的 payload 为什么能生效。这个记录不是为了给别人看,是为了让你在遇到真实测试场景时,能快速判断"这个参数像哪种注入点"。很多人的问题不是不会用工具,是拿到一个真实参数完全不知道该从哪下手,原因就是练习时只记了 payload 没记判断逻辑。

7. 几个容易被忽略但很影响体验的细节

7.1 数据库被写脏后的清理方式

练盲注和堆叠查询的时候,很容易往users表里插数据或者更新数据,练着练着库就乱了,某些关卡的结果会对不上。这时候最省事的做法不是逐条删,而是重新跑一遍初始化脚本,把库重置回干净状态。容器化部署的话直接重启容器更彻底。

养成"练完一批就重置一次"的习惯,比每次手动修数据省太多时间。这一点我在练时间盲注的时候吃过亏——数据库里残留了脏数据,导致某关的判断条件永远为真,卡了很久才发现是环境的问题。

7.2 为什么不要在公网暴露这套环境

SQLi-Labs 从设计之初就是"故意不设防"的,它的所有关卡都在主动接受注入请求,没有任何防护逻辑。放在本机或者隔离的内网环境自学完全没问题,但千万不要把它挂到公网可访问的位置。原因很直接:它不只是一套练习代码,还是个真实运行的 PHP 应用,配置里带着数据库账号密码,一旦被外部扫描到,风险是实打实的。

这条不是危言耸听,是搭建靶场时最基本的安全意识。学习环境就让它安安静静待在本地,需要多人共享时用虚拟机加内网隔离。

7.3 备份一份干净的环境

这是个很小但很省事的建议:环境跑通、数据库初始化成功之后,把整个目录和数据库导出来备份一份。之后不管你怎么折腾搞坏了,恢复一下就是几分钟的事。集成面板的话,把整个靶场文件夹压缩存一份,数据库用面板自带的导出功能导成 sql 文件。

我自己就吃过没备份的亏,有一次调字符集把配置文件改乱了,忘记原始值,翻教程找默认配置找了半天。从那之后每次环境一跑通,第一件事就是备份。

7.4 PHP 错误日志比浏览器报错信息更全

浏览器上看到的那一行报错往往只是冰山一角,真正有用的调用栈在 PHP 错误日志里。集成面板一般都提供日志查看入口,手工环境的话看php.ini里error_log指向的路径。遇到 "Fatal error" 这类问题,去日志里搜对应文件和时间点,能看到完整调用链,定位速度快很多。

顺便说一句,display_errors打开只是临时排错用的,环境稳定之后建议关掉,否则报错信息会暴露路径和结构,这在真实项目里是安全隐患,在靶场里虽然无所谓,但养成"排错时开、上线时关"的习惯没坏处。

最后分享一个我自己的做法:每次重构或迁移环境时,我会先在隔离的目录里把 SQLi-Labs 单独跑一遍最小验证——首页能开、数据库能初始化、Less-1 能正常回显,这三步过了再往下堆其它东西。这样一旦出问题,能立刻判断是新装的部分引入了冲突,还是靶场本身的问题。搭建这种事,变量越少,排查越省心。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询