堆叠查询型SQL注入依赖后端是否允许分号执行多语句,不靠报错或回显,而需检测驱动支持(如mysqli_multi_query、allowMultiQueries=true)、延时响应(SLEEP)、状态变更(INSERT/UPDATE验证)及非参数化拼接风险。

堆叠查询型SQL注入不是靠报错或回显就能直接发现的,它依赖后端是否允许分号执行多语句——这意味着即使页面没异常、没数据回显,漏洞也可能真实存在。识别的关键不在于“有没有反馈”,而在于“能不能执行第二条语句”。
看数据库驱动是否启用多语句执行
堆叠注入能否成功,首先取决于底层驱动是否支持一次提交多条语句。常见组合中:
- PHP + MySQL:
mysqli_query()不支持堆叠;mysqli_multi_query()支持,但默认不用,需显式调用 - Python + MySQLdb/PyMySQL:
cursor.execute()默认只执行单条;cursor.executemany()或手动拆分+循环才可能触发,但非原生堆叠 - Java + JDBC:需在连接URL中显式开启
allowMultiQueries=true,否则Statement.execute()遇到分号直接报错MySQLSyntaxErrorException - Node.js + mysql2:默认禁用,需配置
multipleStatements: true
所以代码里一旦出现 mysqli_multi_query、连接串含 allowMultiQueries=true、或 ORM 显式启用多语句(如 TypeORM 的 queryRunner.query() 直接拼接),就要立刻拉响警报。
测试时别只盯 SELECT 回显
堆叠注入常无声无息——第二条语句(比如 DROP TABLE 或 INSERT)不返回结果,前端根本看不到变化。测试必须绕过“有没有回显”这个思维惯性:
- 用
1; SELECT SLEEP(5)--观察响应延迟,而非内容变化 - 对写操作接口(如修改密码、提交表单)尝试
1'; UPDATE users SET password='test' WHERE id=1--,再单独查该记录验证是否生效 - 若目标有日志功能,尝试
1; INSERT INTO logs(msg) VALUES('test')--,再检查日志表是否新增 - 避免只测
id=1; SELECT version()--——MySQL 堆叠中只有第一条查询结果会返回,后面全丢弃
检查输入是否被拼接到非参数化 SQL 中
所有堆叠注入的前提是用户输入进了 SQL 字符串拼接逻辑。只要看到以下任一模式,风险极高:
"SELECT * FROM users WHERE id = " + user_idf"UPDATE config SET value='{value}' WHERE key='{key}'"String.format("DELETE FROM session WHERE token = '%s'", token)- ORM 中使用
.raw()、.execute()或字符串模板拼接动态表名/字段名
特别注意:哪怕用了参数化查询,如果拼接的是表名、列名、排序字段(如 ORDER BY ${sortField}),依然逃不过堆叠风险——因为占位符无法覆盖这些语法结构。
留意数据库权限与语句类型限制
堆叠注入的实际危害,还受数据库账户权限和语句类型双重制约:
- MySQL 用户若只有
SELECT权限,1; DROP TABLE x会因权限拒绝而失败,但1; SELECT ... UNION SELECT ...仍可能成功 - 某些托管环境(如阿里云RDS、AWS RDS)默认关闭
multi_statements,但自建 MySQL 5.7+ 默认允许,且 PHPmysqli扩展编译时可能启用该特性 -
CREATE、LOAD DATA INFILE、SELECT ... INTO OUTFILE这类敏感语句,在多数生产库已被禁用,但攻击者仍可用INSERT写 Webshell 或扩权
真正容易被忽略的点是:堆叠注入不要求回显,也不依赖错误信息,它靠的是“静默执行”。很多安全扫描器漏报,就是因为只检测报错或布尔差异,而没做延时探测或状态变更验证。

















