Windows下使用Docker搭建SQL注入靶场SQLi-labs的完整指南

📅 2026/7/7 1:03:23 👁️ 阅读次数 📝 编程学习
Windows下使用Docker搭建SQL注入靶场SQLi-labs的完整指南

1. 项目概述与核心价值

最近在和一些刚入行安全的朋友交流时,发现一个挺普遍的现象:大家理论学了不少,知道SQL注入的原理是“把用户输入的数据当成了代码来执行”,但真到了自己动手搭建环境、复现漏洞、一步步调试的时候,就有点无从下手了。网上的教程要么是直接给个现成的虚拟机镜像,黑盒操作,知其然不知其所以然;要么就是步骤过于简略,缺了关键的配置细节,卡在一个报错上能折腾半天。这让我想起了自己早年踩坑的经历,所以今天决定写一篇最“保姆级”的指南,手把手带你从零开始,在Windows系统上,用Docker容器技术,搭建一个经典的SQL注入学习靶场——SQLi-labs。

这个项目的核心价值在于“可控”和“透明”。不同于直接下载一个打包好的、你不知道里面到底改了哪些配置的虚拟机,我们用Docker来部署,意味着整个应用环境(包括操作系统、Web服务器、数据库、PHP版本)都是通过清晰的配置文件(Dockerfile和docker-compose.yml)定义出来的。你可以清楚地看到每一个组件是如何被安装和配置的,甚至可以随时进入容器内部,查看日志、修改代码、调试数据库连接。这对于安全学习者来说至关重要,因为理解漏洞的成因,往往需要你能深入到运行环境的每一个层面去观察。SQLi-labs本身包含了从GET型、POST型到盲注、报错注入、堆叠注入等数十个关卡,几乎涵盖了SQL注入的所有常见类型,是公认的、最好的入门与实践靶场之一。通过亲手搭建它,你不仅能得到一个随时可用的练习环境,更能深刻理解一个典型的“LAMP”(Linux + Apache + MySQL + PHP)应用是如何被组装和运行起来的,这份系统性的理解,是未来你进行漏洞挖掘和代码审计时非常宝贵的基础。

2. 环境准备与工具选型解析

在开始动手之前,我们需要明确整个技术栈和所需的工具。选择在Windows上使用Docker,而不是传统的虚拟机(如VMware)或本地安装WAMP套件,是经过多方面权衡的。

2.1 为什么选择 Docker on Windows?

首先,是环境隔离与纯净性。Docker容器提供了一个与宿主机(你的Windows系统)完全隔离的轻量级虚拟环境。靶场需要的Apache、MySQL、PHP以及SQLi-labs源码都运行在这个容器里,不会污染你本地的PHP或MySQL环境。当你练习结束或想重置时,只需要删除容器和镜像,系统就能恢复如初,干净利落。

其次,是部署的便捷性与可复现性。一旦我们编写好Docker相关的配置文件,在任何一台安装了Docker的Windows电脑上,只需要几条命令,就能在几分钟内复现出一个一模一样的靶场环境。这对于团队分享、教学演示或者在不同机器上切换工作场景,效率提升是巨大的。

最后,是资源消耗与性能。与传统虚拟机需要模拟完整操作系统内核相比,Docker容器与宿主机共享内核,因此启动速度极快,内存和CPU占用也更低。这对于个人学习电脑来说非常友好,你可以在后台运行靶场的同时,流畅地进行其他工作。

2.2 核心工具清单与安装要点

你需要准备以下三样东西:

  1. Docker Desktop for Windows:这是我们的容器运行时引擎。前往Docker官网下载安装包。安装过程中有几个关键点需要注意:

    • 启用 WSL 2 后端:现代版本的Docker Desktop强烈推荐使用WSL 2(Windows Subsystem for Linux 2)作为后端,而不是传统的Hyper-V。这能获得更好的性能和Linux文件系统兼容性。安装程序通常会提示你安装WSL 2内核更新包,务必按照指引完成。
    • 配置资源:安装完成后,打开Docker Desktop设置,在“Resources”选项中,建议为Docker分配至少4GB的内存和2个CPU核心,以确保MySQL等服务运行流畅。
    • 验证安装:安装完成后,打开Windows终端(PowerShell或CMD),运行docker --versiondocker-compose --version(新版本Docker Desktop已集成docker compose插件),确认版本信息正常输出。
  2. 一个代码编辑器:用于编写和查看我们的配置文件。推荐使用VS Code,它轻量且对Docker和配置文件有很好的语法高亮支持。当然,Notepad++、Sublime Text也完全可以。

  3. SQLi-labs 源代码:这是靶场的本体。我们需要从GitHub上获取它。你可以直接访问其GitHub仓库下载ZIP包,或者如果你安装了Git,使用git clone命令会更方便。

注意:在下载和安装Docker过程中,请务必从官方网站获取安装包,避免使用来路不明的第三方修改版本,以确保软件环境的安全和稳定。

3. 靶场搭建的详细步骤拆解

接下来,我们将把整个过程分解为几个清晰的步骤。我会尽量详细地说明每一步的操作和背后的意图,确保即使你是第一次接触Docker,也能顺利跟上。

3.1 获取与组织项目文件

首先,在你电脑上找一个合适的位置创建一个项目文件夹,例如D:\SecurityLabs\SQLi-Labs-Docker。这个文件夹将作为我们所有工作的根目录。

打开终端(PowerShell),进入这个目录,然后使用Git克隆SQLi-labs的源码。如果网络条件不允许或你没有Git,也可以手动去GitHub页面下载ZIP包并解压到此文件夹内。

cd D:\SecurityLabs mkdir SQLi-Labs-Docker cd SQLi-Labs-Docker git clone https://github.com/Audi-1/sqli-labs.git

克隆完成后,你的目录结构应该类似于:

SQLi-Labs-Docker/ └── sqli-labs/ # 从GitHub克隆下来的源码文件夹 ├── sql-connections/ ├── sqli-labs/ ├── LICENSE └── README.md

为了后续Docker构建的清晰,我建议将源码文件夹改名为更通用的www,这符合Web服务器存放网站根目录的惯例。同时,我们在项目根目录下创建两个至关重要的配置文件:Dockerfiledocker-compose.yml

# 在项目根目录 (SQLi-Labs-Docker) 下执行 mv sqli-labs www

现在目录结构变为:

SQLi-Labs-Docker/ ├── www/ # 网站根目录,存放SQLi-labs所有PHP文件 └── (待创建:Dockerfile, docker-compose.yml)

3.2 编写 Dockerfile:定义Web服务镜像

Dockerfile是一个文本文件,里面包含了一系列的指令,用于告诉Docker如何一步步构建出一个满足我们需求的定制化镜像。我们将基于一个轻量的PHP+Apache官方镜像来构建。

在项目根目录下创建Dockerfile文件,内容如下:

# 使用官方 PHP 7.4 版本,并带有 Apache 服务器的镜像作为基础 # 选择7.4是因为SQLi-labs代码较老,在新版PHP8.x上可能有不兼容警告,7.4兼容性最好。 FROM php:7.4-apache # 设置容器内的时区,避免日志时间不对(可选,但建议) RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime # 安装PHP连接MySQL所需的扩展:mysqli 和 pdo_mysql # SQLi-labs使用mysql_*函数和mysqli,安装这两个扩展是必须的。 RUN docker-php-ext-install mysqli pdo_mysql # 启用Apache的rewrite模块,虽然SQLi-labs不一定需要,但为后续可能的功能扩展准备 RUN a2enmod rewrite # 将宿主机当前目录下的 `www/` 文件夹,复制到容器内的 `/var/www/html/` # 这样,我们本地的SQLi-labs代码就变成了容器中Apache服务的网站根目录。 COPY www/ /var/www/html/ # 修改Apache的配置文件,允许 .htaccess 文件覆盖配置(针对某些特定关卡可能需要) # 并设置网站根目录的权限,让Apache进程有权读写。 RUN echo "<Directory /var/www/html/>\n AllowOverride All\n Require all granted\n</Directory>" > /etc/apache2/conf-available/sqli-labs.conf \ && a2enconf sqli-labs \ && chown -R www-data:www-data /var/www/html # 声明容器运行时暴露的端口,Apache默认是80 EXPOSE 80

这个Dockerfile做了几件关键事:1) 选定基础环境;2) 安装必要的PHP扩展;3) 把我们的代码复制进去;4) 进行一些基本的Apache配置。它定义了一个“蓝图”,基于这个蓝图可以生成一个包含了完整SQLi-labs运行环境的镜像。

3.3 编写 docker-compose.yml:编排多服务应用

SQLi-labs靶场不仅需要Web服务器(PHP+Apache),还需要一个MySQL数据库服务。docker-compose.yml文件的作用就是定义和编排多个相关联的容器,让它们能够协同工作。

在项目根目录创建docker-compose.yml文件,内容如下:

version: '3.8' services: # Web 服务,命名为 web web: # 使用当前目录下的 Dockerfile 来构建镜像,而不是从仓库拉取现成的 build: . # 容器名称,方便识别 container_name: sqli-labs-web # 端口映射:将宿主机的 8080 端口映射到容器的 80 端口 # 这样,你在浏览器访问 localhost:8080 就能访问到靶场 ports: - "8080:80" # 数据卷挂载:将本地的 `www/` 目录挂载到容器的网站根目录 # 这样做的好处是,你可以在宿主机上直接修改PHP代码,容器内会实时生效,无需重建镜像。 volumes: - ./www:/var/www/html # 依赖于 db 服务,确保数据库先启动 depends_on: - db # 设置环境变量,告诉PHP应用数据库的连接信息 # 这里的 `db` 是下面定义的数据库服务名,在Docker网络内可以直接通过主机名`db`访问 environment: - DB_HOST=db - DB_USER=root - DB_PASSWORD=your_strong_password_here # 务必修改成一个强密码! # 连接到自定义网络,方便服务间通信 networks: - sqli-network # 数据库服务,命名为 db db: # 使用 MySQL 5.7 官方镜像。选择5.7是为了与SQLi-labs的代码保持最佳兼容性。 image: mysql:5.7 container_name: sqli-labs-db # 重启策略:除非手动停止,否则总是重启。避免容器意外退出后靶场不可用。 restart: always # 环境变量,用于初始化MySQL的root密码和创建默认数据库 environment: - MYSQL_ROOT_PASSWORD=your_strong_password_here # 必须与上面web服务中的密码一致! - MYSQL_DATABASE=security # 数据卷挂载:将MySQL数据持久化到宿主机,避免容器删除后数据丢失。 volumes: - mysql_data:/var/lib/mysql # 连接到同一个自定义网络 networks: - sqli-network # 定义数据卷,用于持久化MySQL数据 volumes: mysql_data: # 定义自定义网络,隔离本项目的容器通信 networks: sqli-network: driver: bridge

这个配置文件定义了两个“服务”:webdb。它们会分别启动一个容器,并且通过sqli-network这个内部网络连接在一起。web容器可以通过主机名db直接访问到db容器中的MySQL服务,这正是我们在environment中设置DB_HOST=db的原因。端口映射8080:80让你能从外部访问,而数据卷挂载则保证了代码修改的即时性和数据的安全性。

3.4 修改 SQLi-labs 数据库配置文件

SQLi-labs的源码里有一个数据库连接配置文件,默认是连接本地(localhost)的MySQL。现在我们的数据库运行在另一个名为db的容器里,所以需要修改这个配置。

找到www/sql-connections/db-creds.inc文件(或者类似名称的文件,不同版本可能略有差异),用编辑器打开。你会看到类似下面的代码:

<?php //give your mysql connection username n password $dbuser ='root'; $dbpass =''; $dbname ="security"; $host = 'localhost'; $dbname1 = "challenges"; ?>

我们需要将其修改为与docker-compose.yml中环境变量一致的信息:

<?php // Modified for Docker environment $dbuser ='root'; $dbpass ='your_strong_password_here'; // 与docker-compose.yml中的MYSQL_ROOT_PASSWORD一致 $dbname ="security"; $host = 'db'; // 关键!将 localhost 改为数据库服务名 db $dbname1 = "challenges"; ?>

重要提示$host的值必须改为'db'。在Docker Compose创建的网络中,服务名(db)可以直接作为主机名被其他服务(web)解析。这是容器间通信的关键。

3.5 启动靶场与初始化数据库

所有配置完成后,我们就可以启动整个靶场了。在项目根目录(即docker-compose.yml所在的目录)打开终端。

  1. 构建并启动服务

    docker-compose up -d

    -d参数表示“后台运行”。这条命令会执行以下操作:

    • 根据Dockerfile构建web服务的镜像(第一次运行时会下载基础镜像,需要一些时间)。
    • 从Docker Hub拉取mysql:5.7镜像(如果本地没有)。
    • 创建sqli-network网络和mysql_data数据卷。
    • 按依赖顺序启动dbweb容器。
  2. 查看运行状态

    docker-compose ps

    你应该看到webdb两个容器的状态都是Up

  3. 初始化数据库: 容器启动后,数据库是空的,我们需要运行SQLi-labs自带的脚本来创建数据表和填充测试数据。

    • 方法一:通过浏览器访问http://localhost:8080(如果你映射的是8080端口)。页面通常会有一个链接或提示让你“Setup/reset Database”,点击它。这会执行www/sql-connections/setup-db.php之类的脚本。
    • 方法二:如果页面没有自动跳转或初始化失败,可以手动执行。首先进入web容器的命令行:
      docker exec -it sqli-labs-web /bin/bash
      然后在容器内,使用curl命令或直接访问本地URL来触发初始化脚本:
      curl http://localhost/setup-db.php # 具体路径请查看www目录下的文件
      或者,你也可以直接使用MySQL客户端连接数据库容器手动导入SQL文件(位于www/目录下),但这相对复杂。

    当看到“Database setup successful…”或类似的成功提示时,说明数据库已就绪。

  4. 访问靶场: 打开浏览器,访问http://localhost:8080。你应该能看到SQLi-labs的首页,上面列出了所有的挑战关卡(Less-1, Less-2, …)。至此,靶场搭建成功!

4. 核心配置原理解析与深度定制

搭建成功只是第一步,理解每一步配置背后的原理,才能让你在遇到问题时有能力排查,甚至根据自己需求进行定制。

4.1 Docker网络模式与容器间通信

这是本项目最核心的原理之一。在docker-compose.yml中,我们定义了一个名为sqli-network的自定义桥接网络。当webdb容器都加入这个网络后,它们之间就形成了一个隔离的、可相互通信的小型局域网。

  • 服务名即主机名:Docker内置的DNS服务允许容器通过服务名(db)来解析到对应容器的IP地址。这就是为什么我们在PHP配置文件中可以把$hostlocalhost改成dbweb容器内的PHP代码尝试连接db:3306时,Docker网络会将其正确路由到运行MySQL的容器。
  • 与宿主机隔离:这个网络默认与宿主机网络是隔离的。宿主机(你的Windows)不能直接通过db这个主机名访问数据库容器,这增加了安全性。如果你想从宿主机上的数据库管理工具(如Navicat、MySQL Workbench)连接,需要映射数据库端口,例如在db服务下添加ports: - "3307:3306",然后连接localhost:3307

4.2 数据卷挂载:代码热更新与数据持久化

我们使用了两种类型的数据卷挂载:

  1. 绑定挂载(Bind Mount)./www:/var/www/html

    • 这是将宿主机的./www目录直接挂载到容器内的/var/www/html。任何在宿主机上对该目录文件的修改,都会立即反映在容器内,反之亦然。
    • 好处:开发调试神器。你可以用VS Code在Windows上修改PHP代码,保存后,刷新浏览器就能立刻看到效果,无需重启容器或重建镜像。
    • 注意:要确保宿主机目录的路径正确,并且文件权限不会导致Apache无法读取(我们之前在Dockerfile中用chown命令处理了容器内的权限)。
  2. 命名卷(Named Volume)mysql_data:/var/lib/mysql

    • mysql_data是一个由Docker管理的命名卷,它存储在宿主机上一个Docker控制的区域(通常不是直接可见的普通路径)。
    • 好处:数据持久化。即使你执行了docker-compose down删除了db容器,只要这个卷没有被删除,你的所有数据库数据(包括你练习时创建的表、插入的数据)都会保留。下次docker-compose up -d时,新的db容器会挂载这个卷,数据完好无损。
    • 管理:你可以通过docker volume ls查看所有卷,docker volume rm mysql_data来删除它(这会清空所有数据,慎用)。

4.3 镜像版本选择与兼容性考量

在Dockerfile和Compose文件中,我们特意选择了php:7.4-apachemysql:5.7这两个并非最新的版本,这是出于兼容性的慎重考虑。

  • PHP 7.4:SQLi-labs项目年代较早,其代码中使用了如mysql_*等已被废弃的函数(在PHP7.0+中已移除,但mysqli扩展兼容)。虽然PHP 8.x可以通过调整错误报告级别来运行,但可能会遇到一些弃用警告或细微的行为差异,对初学者造成干扰。PHP 7.4是最后一个对老代码兼容性非常好的长期支持(LTS)版本之一,能确保靶场所有关卡稳定运行。
  • MySQL 5.7:同样,选择广泛使用、稳定的5.7版本,可以避免因MySQL 8.0默认身份认证插件(caching_sha2_password)变更而导致的PHP老驱动连接失败问题。MySQL 5.7使用的mysql_native_password插件与PHP的mysqlipdo_mysql扩展兼容性最好。

5. 常见问题排查与实战调试技巧

即使按照步骤操作,也可能会遇到一些问题。这里我总结了一些常见的坑和解决方法。

5.1 容器启动失败排查

如果docker-compose up -d后,docker-compose ps显示容器状态不是Up,或者很快退出。

  • 查看日志:这是最重要的排查手段。

    # 查看所有服务的日志 docker-compose logs # 查看特定服务(如db)的日志 docker-compose logs db # 持续跟踪日志输出 docker-compose logs -f web
    • 常见错误1:web容器启动后立即退出。查看日志很可能发现Apache启动失败。原因可能是Dockerfile中命令执行出错(如安装扩展失败),或者www目录挂载后权限问题。检查Dockerfile每条RUN命令是否都能成功执行。可以尝试先注释掉COPY和后面的命令,构建一个基础镜像看Apache能否运行。
    • 常见错误2:db容器不断重启。查看日志可能显示MySQL初始化失败。很可能是环境变量MYSQL_ROOT_PASSWORD包含特殊字符(如$,!)导致shell解析问题。尝试使用纯字母数字组合的密码,或者用单引号将密码括在环境变量值里(在Compose文件YAML语法中需注意转义)。
  • 进入容器内部检查

    # 进入web容器的bash环境 docker exec -it sqli-labs-web /bin/bash # 进入后,可以检查Apache是否运行 ps aux | grep apache # 检查PHP扩展是否加载 php -m | grep mysqli # 检查网站根目录文件是否存在 ls -la /var/www/html/ # 尝试手动连接数据库,测试网络和凭据 mysql -h db -u root -p

    通过内部检查,可以直观地看到文件、进程、网络连接的状态。

5.2 浏览器访问报错排查

能访问首页,但点击“Setup/reset Database”或进入具体关卡时报错。

  • 数据库连接错误

    • 症状:页面显示“Error connecting to the database”。
    • 排查
      1. 确认www/sql-connections/db-creds.inc文件中的$host已改为'db',密码与Compose文件一致。
      2. 进入web容器,尝试用mysql命令行连接:mysql -h db -u root -p,输入密码看能否成功。如果失败,说明网络或数据库服务有问题。
      3. 检查db容器是否正常运行:docker-compose ps,查看MySQL日志。
      4. 确认数据库是否已初始化。可以手动执行初始化SQL文件。进入db容器:docker exec -it sqli-labs-db bash,然后登录MySQL:mysql -u root -p,执行source /path/to/sql/file.sql(需要先将SQL文件复制到容器内,或通过挂载卷访问)。
  • PHP函数未定义或弃用警告

    • 症状:页面显示“Fatal error: Call to undefined function mysql_connect()”或大量弃用警告。
    • 解决:这确认了PHP版本兼容性问题。确保你的Dockerfile使用的是php:7.4-apache,并且安装了mysqli扩展。对于弃用警告,可以在PHP代码开头或php.ini中设置error_reporting(E_ALL ^ E_DEPRECATED);来屏蔽,但不建议在生产环境这么做,学习环境为了方便可以暂时屏蔽。

5.3 性能优化与日常管理

  • 停止与启动

    # 停止所有服务(容器停止,但未删除) docker-compose stop # 启动已停止的服务 docker-compose start # 停止并删除所有容器、网络(数据卷默认保留) docker-compose down # 停止并删除所有容器、网络、以及自定义的命名卷(数据会丢失!) docker-compose down -v
  • 重置靶场数据: 如果想清空所有练习数据,回到初始状态,有两种方法:

    1. 浏览器访问http://localhost:8080,通常首页有重置链接。
    2. 更彻底的方式:docker-compose down -v删除容器和MySQL数据卷,然后重新docker-compose up -d并初始化数据库。注意:这会丢失所有数据。
  • 备份数据库: 由于使用了命名卷,你可以很方便地备份整个MySQL数据目录。找到mysql_data卷在宿主机上的实际路径(可通过docker volume inspect mysql_data查看Mountpoint),然后复制该目录下的文件即可。更规范的方式是使用mysqldump命令从容器内导出SQL文件。

6. 安全须知与靶场使用建议

最后,必须强调几个重要的安全和使用原则。

  1. 仅用于本地学习:这个靶场环境绝对不要暴露在公网(如云服务器)上。SQLi-labs本身充满漏洞,且我们使用了弱密码或默认配置,一旦公网可访问,极有可能被黑客瞬间攻陷并作为跳板机。
  2. 修改默认密码:教程中使用了示例密码your_strong_password_here,你在实际搭建时,务必将其修改为一个自己独有的、复杂的密码,并同时在docker-compose.ymldb-creds.inc文件中更新。
  3. 理解漏洞原理而非单纯通关:使用SQLi-labs时,不要只追求“注入出数据”。对于每一关,尝试:
    • 查看前端源代码,理解参数是如何传递的。
    • 查看后端PHP源码(就在www/sqli-labs/目录下),分析其SQL查询语句的拼接逻辑。
    • 使用Burp Suite或浏览器开发者工具拦截请求,手动修改参数,观察不同的输入会导致怎样的响应和报错信息。
    • 思考修复方案:如何修改代码来防御这种注入?
  4. 结合其他工具:在练习盲注等复杂类型时,可以配合使用sqlmap等自动化工具进行验证和学习。但务必在本地环境操作。

搭建这个环境的过程本身,就是一次对Web应用架构、服务部署和网络安全基础概念的绝佳实践。当你看到浏览器中成功显示出SQLi-labs的首页时,你收获的不仅仅是一个靶场,更是一套可复用的、基于现代容器化技术的实验环境搭建方法论。这套方法可以轻松迁移到其他Web漏洞靶场(如DVWA、bWAPP)的搭建上。希望这篇详尽的指南能帮你扫清入门路上的障碍,更专注地投入到SQL注入技术本身的学习与探索中。如果在搭建过程中遇到任何问题,回顾日志和检查配置文件永远是第一步,祝你学习顺利。