
搭建 SqliLabs 这件事说简单是真简单——源码往 Web 根目录一扔改一行数据库账号密码浏览器打开就能跑说麻烦也是真麻烦我见过太多人卡在一句Fatal error: Call to undefined function mysql_connect()或者一片纯白页面上从下午折腾到半夜最后开始怀疑是压缩包下载坏了。这篇就把我在 Windows 和 Linux 两边反复搭 SqliLabs 时踩过的坑摊开讲一遍从环境选型的思路、版本匹配的底层逻辑到数据库初始化、连接文件配置、报错逐条排查尽量写成一份能直接照着抄的排查手册。内容主要面向刚接触 Web 安全、准备自己搭个本地练习环境的朋友也适合已经搭起来但卡在某一关跑不通的人。所有操作都建议只在自己本机或者有明确授权的实验环境里做别拿这些手法去碰任何不属于你的系统这个前提先摆在前面。下面按想清楚再动手的顺序来说。1. 搭建前的整体思路与选型为什么SqliLabs总在环境上翻车1.1 先搞清楚SqliLabs到底依赖什么很多人搭不起来根源在于把 SqliLabs 当成一个普通的静态网站。它不是。它是一堆 PHP 脚本 一个 MySQL 数据库的组合里面每一关都是一个独立的 PHP 页面页面里写死了 SQL 查询语句靠 URL 参数拼接进查询里从而制造出可控的注入点。所以它同时强依赖三样东西一个能解析 PHP 的 Web 服务器、一个能连上的 MySQL 服务、以及 PHP 与 MySQL 之间的那层扩展。这三样东西里任何一环版本对不上页面就是打不开。最典型的就是那一整套mysql_*开头的老函数——mysql_connect()、mysql_query()、mysql_error()。这批函数在 PHP 5.5 就被标记为废弃PHP 7.0 直接被移除。而 SqliLabs 的原始版本恰恰大量使用它们于是当你在 PHP 7 或 PHP 8 环境下打开 Less-1页面直接抛致命错误白屏或者报Call to undefined function。这不是你操作错了是版本天生不兼容。搞明白这个依赖关系后面的所有决策就都好理解了你选的 PHP 版本决定了你需要哪个分支的 SqliLabs你选的 MySQL 版本决定了认证方式会不会拦你你选的集成环境决定了改配置的入口在哪。1.2 集成环境还是手工编译两条路怎么选我个人建议新手一律走集成环境这条路比如 Windows 下的 phpStudy、XAMPP、WampServerLinux 下用 apt 装 lamp 那一套。理由很实在SqliLabs 本身的价值在于练注入思路不在于练环境配置。集成环境自带 Apache PHP MySQL还带一个图形化的版本切换面板能让你在 PHP 5.6 和 PHP 7.4 之间一键切换这对 SqliLabs 来说简直是刚需。手工编译适合什么人适合你已经能熟练处理httpd.conf、php.ini、my.cnf这三个文件并且清楚LoadModule、extension_dir、default_authentication_plugin这些配置项各自管什么。如果你目前还分不清php.ini和httpd.conf的职责边界那就别折磨自己先用集成环境把靶场跑起来把注入练明白回头再去啃源码编译。还有一种更省事的路子是用容器。现成的 Web 环境镜像拉下来把 SqliLabs 目录挂进去几分钟就能跑起来环境隔离也干净玩坏了直接删容器重来。代价是你得先对容器有一点基本概念知道端口映射和目录挂载是怎么回事。1.3 版本匹配是搭建成败的分水岭我把这几年的经验压缩成一句话PHP 版本决定你能不能跑起来MySQL 版本决定你能不能连上Web 服务器端口决定你能不能访问到。具体到组合上可以参照下面这张表来选能避开绝大多数坑方案PHP 版本MySQL 版本适合人群备注原版 SqliLabs PHP 5.65.65.5 / 5.7想跑通全部 65 关最省心老函数全在兼容分支 PHP 7.x7.0–7.45.7不想装老 PHP需下载 PHP7 兼容版本兼容分支 PHP 8.x8.08.0追求新环境部分关卡仍需微调容器方案镜像自带镜像自带熟悉容器的人隔离好删了不心疼选 PHP 5.6 的理由很直接它保留了完整的mysql_*函数族你下载官方原版压缩包解压即用不需要改任何一行 PHP 源码。选 PHP 7 的理由是现在的集成环境默认就是 7.x但你必须去找那个把mysql_*改成mysqli_*的兼容分支并且确认兼容分支覆盖了你想要练的关卡范围。注意不要在同一台机器上同时装两个集成环境去抢 80 端口端口冲突是新手最常遇到的玄学问题之一后面会专门讲怎么排查。2. 环境准备与关键配置的底层逻辑2.1 PHP版本与mysql扩展的生死关系先把这件事讲透因为它是 90% 白屏问题的源头。PHP 连接 MySQL 有三代扩展mysql_*第一代过程式PHP 5.5 废弃PHP 7.0 移除。mysqli_*第二代支持面向对象和过程式至今可用。PDO_MySQL第三代跨数据库统一接口至今可用。SqliLabs 原版几乎清一色用第一代。所以当你在 PHP 7 环境打开页面PHP 引擎会告诉你找不到mysql_connect这个函数——不是没连上数据库是连函数本身都不存在了。这时候你有两条路第一条把 PHP 降到 5.6。集成环境面板里一般都有PHP 版本下拉框选 5.6点应用重启 Apache。但要注意切换版本的时候 Apache 需要重新加载对应的模块改完不重启是无效的。第二条换兼容分支。社区里有人把源码里的mysql_*全部替换成了mysqli_*并处理了参数顺序差异。这条路的前提是你能找到靠谱的分支并且它的关卡目录完整。不管走哪条路都要确认php.ini里对应的扩展是打开的。检查方法是在 Web 根目录放一个phpinfo.php内容就一行?php phpinfo(); ?浏览器访问它搜索mysql关键字。如果你看到mysqli的配置块说明第二代扩展已启用如果连mysql相关的块都搜不到那就去php.ini里找extensionmysqli把前面的分号去掉保存后重启 Apache。提示phpinfo.php用完就删掉留在公网可访问的目录里等于把服务器家底摊给所有人看。2.2 MySQL版本与认证插件的兼容陷阱这一条很多人第一次遇到会懵。MySQL 8.0 把默认的认证插件从mysql_native_password换成了caching_sha2_password。老的 PHP 客户端库不认识这个新插件于是在连接阶段直接报错典型信息是The server requested authentication method unknown to the client或者Authentication plugin caching_sha2_password cannot be loaded。现象是数据库服务明明启动了用命令行客户端能登上Navicat 也能连但网页就是连不上。这时候你会误以为是db-creds.inc里的密码写错了反复改密码改到怀疑人生。其实跟密码没关系。解决办法是把对应用户的认证插件改回老的ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY 你的密码; FLUSH PRIVILEGES;或者更彻底一点在 MySQL 配置文件my.iniWindows或my.cnfLinux的[mysqld]段里加上[mysqld] default_authentication_pluginmysql_native_password改完重启 MySQL 服务。我个人更推荐第二种一次性解决后续新建用户也不会再踩这个坑。还有一种情况是字符集。MySQL 8 默认utf8mb4MySQL 5.7 默认可能是latin1。如果导入 SQL 文件时编码对不上数据库里存的中文会变成问号虽然不影响注入练习但看着膈应。导入前统一把连接字符集设成utf8或者导入命令里显式指定mysql -u root -p --default-character-setutf8 security.sql2.3 php.ini里几个必须动的开关SqliLabs 是练习靶场你的目标是看到错误信息所以有几个配置跟生产环境的常规建议是反着来的。第一个是display_errors。默认在生产环境是关的页面出错只给你一个空白或者 500。练注入的时候必须打开否则报错注入那一类关卡根本没法做因为页面上不显示任何数据库报错。display_errors On error_reporting E_ALL第二个是short_open_tag。部分老版本 PHP 代码里用的是?短标签而不是?php如果short_open_tag Off这些文件里的 PHP 代码不会被解析直接当纯文本吐到页面上。现象是你看到页面上一堆 PHP 源码原封不动显示出来这基本就是这个开关的问题。short_open_tag On第三个是时区。date.timezone没设置的话PHP 会抛一个警告虽然不影响功能但会污染页面输出干扰你看真正的报错信息。date.timezone Asia/Shanghai这三个改完老规矩重启 Apache。顺带说一句改php.ini之后不生效八成是你改的不是当前生效的那一份。一台机器上装多个 PHP 版本时每个版本目录下都有一份自己的php.iniphpinfo()页面里有一行Loaded Configuration File那才是真正生效的路径照着这个路径去改。2.4 目录权限与杀毒软件的隐形干扰Windows 上还有一类特别隐蔽的问题杀毒软件。SqliLabs 的源码里包含大量明显的注入 payload、webshell 片段、以及各种测试用的恶意字符串。实时防护一扫直接把这些文件隔离或者删掉结果就是你解压完发现有 65 个关卡目录其中几个莫名其妙是空的或者sql-connections目录里的文件少了一个页面加载到一半报require_once(): Failed opening required。处理办法很土但很有效把整个 SqliLabs 目录加进杀软的信任区/排除列表然后再重新解压一遍。别嫌麻烦这一步不做后面排查到天亮都找不到原因。Linux 下则是权限问题。Apache 默认以www-dataDebian 系或apacheRedHat 系用户运行如果目录属主是 root 且其他用户没有读权限访问就是 403 Forbidden。sudo chown -R www-data:www-data /var/www/html/sqli-labs sudo chmod -R 755 /var/www/html/sqli-labs755 意味着所有人可读可执行、只有属主可写对靶场来说足够。别图省事给 777那只是在制造新的习惯性恶习。3. 完整搭建流程从零到跑通第一关3.1 站点根目录的位置选择与放置集成环境都有自己的 Web 根目录phpStudy 一般是phpstudy_pro/WWWXAMPP 是xampp/htdocsLinux 上是/var/www/html。把解压出来的sqli-labs整个文件夹丢进去最终结构应该长这样WWW/ └── sqli-labs/ ├── index.html ├── sql-connections/ │ ├── db-creds.inc │ ├── setup-db.php │ └── ... ├── Less-1/ ├── Less-2/ └── ... Less-65/这里有个小细节解压的时候经常会出现双层嵌套目录变成sqli-labs/sqli-labs/Less-1。访问的时候路径就要多写一层很多人以为是 404 是配置问题其实只是目录套了两层。解压后先进去看一眼层级该拍平的拍平。访问地址是http://localhost/sqli-labs/或者http://127.0.0.1/sqli-labs/。用localhost和127.0.0.1在某些环境下行为不完全一致——前者可能走 IPv6 的::1而 MySQL 或者 Apache 的监听配置只绑了 IPv4。如果localhost打不开而127.0.0.1能打开就是这个原因。统一用127.0.0.1可以少一类麻烦。提示不要直接双击Less-1/index.php用文件方式打开那是file://协议PHP 代码不会被解析你只会看到源码原文。3.2 数据库初始化setup-db到底做了什么SqliLabs 目录里有个sql-connections/setup-db.php这是整个靶场的数据库初始化脚本。它做的事情大致是连接 MySQL创建名为security的数据库有些版本还会创建challenges库然后建表、插入初始数据。建出来的核心表包括users、emails、uagents、referers这几张分别对应不同的注入关卡场景——比如uagents表是给 User-Agent 注入用的referers表是给 Referer 注入用的。运行方式是浏览器访问http://127.0.0.1/sqli-labs/sql-connections/setup-db.php如果脚本执行成功你会在页面上看到类似成功创建数据库和表的提示具体措辞各版本略有差异。执行失败的话页面通常会直接抛出数据库连接错误这时候问题就回到db-creds.inc上去了。有些版本没有这个自动脚本或者脚本只建表不插数据那就需要手动导入 SQL 文件mysql -u root -p -e CREATE DATABASE security DEFAULT CHARACTER SET utf8; mysql -u root -p security security.sql导入完用一条命令验证USE security; SHOW TABLES; SELECT COUNT(*) FROM users;users表里应该有十几条记录如果COUNT(*)返回 0说明数据没进去注入练习时联合查询查不出东西你会误以为是自己的 payload 写错了。3.3 db-creds.inc那一行配置改对了吗这个文件是 SqliLabs 的数据库凭据配置路径在sql-connections/db-creds.inc。内容大致是这样?php $host localhost; $dbuser root; $dbpass root; $dbname security; ?四个变量逐一确认$host建议写127.0.0.1而不是localhost理由同上$dbuser和$dbpass必须和你 MySQL 里的实际账号密码一致集成环境常见的是 root/root 或者 root/空$dbname一般保持security不动除非你手动导库时改了名字。这里失败的经典表现是访问关卡页面页面顶部弹出一行Connection failed: Access denied for user rootlocalhost。看到这个就是在告诉你账号或者密码不对或者这个用户没有从localhost连接的权限。注意rootlocalhost和root127.0.0.1在 MySQL 里是两个不同的账号条目权限是分开授予的。如果确认密码没错但还是被拒可以在 MySQL 里查一下SELECT user, host, plugin FROM mysql.user WHERE user root;看看有没有127.0.0.1这个 host 的记录没有就补一条授权。3.4 判断搭建是否真正成功别急着从 Less-1 开始打。先做三步验证。第一步访问http://127.0.0.1/sqli-labs/应该能看到一个带关卡列表的首页这是最基本的 Web 服务通了。第二步访问 Less-1http://127.0.0.1/sqli-labs/Less-1/?id1页面应该正常显示一个用户名和密码。如果这一步就白屏回头去看 2.1 节的 PHP 版本问题。第三步故意制造一个语法错误http://127.0.0.1/sqli-labs/Less-1/?id1如果在id1后面加一个单引号页面回报一个You have an error in your SQL syntax的数据库错误恭喜环境完全正常而且报错回显也是打开的报错注入类关卡可以正常练。这三步都过了后面 65 关的环境基础就打好了。哪一步卡住就照着对应章节排查。4. 高频问题排查实录那些让人抓狂的报错4.1 常见报错速查表先上一张表遇到问题先对号入座再看后面的详细分析。报错信息 / 现象根本原因解决方向Call to undefined function mysql_connect()PHP 7 移除了老扩展降到 PHP 5.6 或换兼容分支页面纯白什么都没有display_errors关闭错误被吞打开错误显示或查 Apache 错误日志Access denied for user账号密码错或 host 权限不足核对db-creds.inc补 host 授权Authentication plugin caching_sha2_passwordMySQL 8 新认证插件改回mysql_native_password页面上直接显示 PHP 源码短标签未开启short_open_tag On403 Forbidden目录权限或索引未开chownOptions Indexes404 Not Found路径错误或目录嵌套检查目录层级和大小写No database selected数据库未创建或名称不符跑setup-db.php或手动建库查询结果为空users表没数据手动导入数据 SQL中文变问号字符集不匹配用 utf8 重新导入4.2 白屏、403、404、500分别怎么查白屏是最难查的因为它什么都不告诉你。第一反应应该是去翻日志而不是对着空白页发呆。Apache 的错误日志位置Linux 下是/var/log/apache2/error.log或/var/log/httpd/error_logWindows 集成环境一般在Apache/logs/error.log。日志里通常会明确写着是哪一行代码抛了什么错比页面上猜半天高效得多。如果日志里也没有那基本可以确定是 PHP 的错误显示被关掉了。把display_errors打开重新访问错误就出来了。403 Forbidden有两种。一种是权限问题前面已经讲过用chown和chmod处理。另一种是目录索引没开——你访问的是目录而不是具体文件Apache 没有index.php就拒绝列出目录内容。检查httpd.conf或者站点配置里的Options需要有Indexes或者指向正确的默认首页文件。SqliLabs 每个关卡目录里都有index.php正常不该出现这种情况所以 403 更大概率是权限。404 Not Found基本只有两个原因路径写错或者目录嵌套。再就是大小写问题Linux 下文件系统区分大小写less-1和Less-1是两个东西URL 里必须跟目录名完全一致。500 Internal Server Error最典型的原因是.htaccess配置里引用了服务器没加载的模块。SqliLabs 某些版本目录里带.htaccess如果里面的指令 Apache 不认识整个目录就 500。可以临时把.htaccess重命名成.htaccess.bak试试能访问了就是它的问题。4.3 端口冲突80被占了怎么办这个问题值得单拎出来讲因为它的表现形式特别有迷惑性——所有服务看起来都启动了数据库连得上但浏览器就是打不开页面或者打开的是另一个完全不相干的页面。原因是你机器上还有别的东西在监听 80 端口。常见的抢占者包括另一个 Web 服务器IIS、某些后台服务、以及各种做本地网络共享的软件。Windows 上查起来很简单netstat -ano | findstr :80输出的最后一列是进程 PID用任务管理器找到对应 PID 结束掉或者干脆改 Apache 的监听端口。改端口在httpd.conf里Listen 8080改完 URL 就要带上端口号http://127.0.0.1:8080/sqli-labs/。改端口的时候记得有个坑如果你配置了虚拟主机VirtualHost那一行的端口也要同步改只改Listen会出现请求打到默认站点上去的情况。Linux 下换一条命令sudo lsof -i :80 # 或者 sudo netstat -tlnp | grep :804.4 关卡特有的那些不是搭错了的现象环境跑通之后练习过程中还会遇到一些看起来像报错、实际是设计如此的情况提前说清楚能省下不少时间。第一个是堆叠注入类关卡。Less-38 往后的部分关卡涉及多语句执行需要 PHP 的mysqli扩展支持multi_query。如果你的 PHP 是老版本用mysql_*实现的堆叠注入可能执行不了。这不是你 payload 的问题。第二个是挑战模式。Less-54 之后的关卡是 challenge 模式表名和列名是随机生成的而且每次提交答案之后后台会把数据库重置一遍。所以你会发现在这几关里刷新一下页面之前查到的表名就变了。这是设计如此逼你每次重新枚举。第三个是注入点类型混淆。Less-1 到 Less-10 是 GET 型Less-11 之后开始有 POST 型再往后还有 Cookie 注入、User-Agent 注入、Referer 注入。很多人卡在 Less-18 之类的关卡上反复改 URL 参数没反应其实是因为注入点在 HTTP 请求头里得用抓包工具改请求头才行改 URL 当然没效果。第四个是转义处理。部分关卡对输入做了转义或者过滤你按前面关卡的思路直接怼 payload 会失败。这不是环境问题是关卡本身在考你绕过思路。提示遇到某关怎么打都不通先看这一关的源码。SqliLabs 的每一关源码都摆在那里index.php里就那几行 SQL 拼接看两眼就知道注入点在哪、过滤了什么。这比盲试 payload 高效十倍。5. 让靶场跑得更稳的维护心得5.1 快照、备份与环境隔离搭一次不容易别让它轻易崩。最值得做的一件事是环境配好、数据库初始化完成之后立刻给整机打一个快照或者至少把 SqliLabs 目录和数据库导出文件各备一份。mysqldump -u root -p --databases security challenges sqli_backup.sql为什么要备份因为你在练习过程中一定会手滑。比如试着用DROP TABLE这类语句做堆叠注入练习一不小心真把表删了或者改db-creds.inc的时候改错了没记下来。有了备份恢复就是两条命令的事mysql -u root -p sqli_backup.sql另外强烈建议把靶场放在虚拟机或者独立环境里别跟日常办公、存资料的系统混在一起。靶场里跑的都是注入、文件包含这类测试代码跟真实数据放在同一台机器上风险和麻烦都不划算。5.2 一台机器上多版本的和平共处如果你同时还要练其他的 Web 靶场可能会遇到版本需求冲突——有的要 PHP 5.6有的要 PHP 7.4。集成环境提供了多版本目录切换是改httpd.conf里的模块引用路径或者用面板一键切。但切换后必须重启服务而且切换前先确认原来的站点配置不会互相干扰。数据库这边不同靶场用不同的库名是个好习惯security、dvwa、pikachu各用各的避免一个靶场的初始化脚本把另一个的数据覆盖了。有些靶场的初始化脚本真的会DROP DATABASE再重建共用库名就是自找麻烦。注意如果你换过端口、改过目录、动过php.ini记得每次改完都做一次完整的回归验证——首页能不能开、Less-1 能不能出结果、报错能不能回显。只测一步很容易漏掉连带影响。5.3 从能跑到跑得明白环境搭通只是起点。真正让这段时间花得值钱的是搭完之后回头把每一类的原理弄清楚。比如同样是报错注入为什么extractvalue()和updatexml()能报出数据、能报出多少位、为什么有时候要配合limit一条条取同样是布尔盲注为什么用ascii()和substr()组合就能一位一位地猜出库名。这些理解靠背 payload 是背不出来的。我自己的习惯是每打完一关就去看一眼源码把这一关的 SQL 语句抄下来标注出注入点在哪、过滤了什么、我的 payload 是怎么绕过去的。同一个关卡隔一周再做一遍如果还能顺畅打下来说明是真的懂了而不是照着答案抄了一遍。还有个小技巧把setup-db.php的访问地址存成书签。练到一半发现数据被自己搞乱了点一下就能重置到初始状态比手动导库快得多。但记住重置会把你自己建的表、插的数据一并清掉重置前想清楚。环境这东西搭一次踩一遍坑第二遍就顺了。真正费时间的从来不是点几下鼠标而是不知道报错背后是什么在作祟。把上面这些对应关系理清楚下次再遇到mysql_connect找不到、密码明明对却连不上、页面白得发光心里就有谱了。