堆叠查询注入更危险,因其利用mysqli_multi_query()等函数一次执行多条sql命令,如admin'; drop table users; --可直接删表,远超普通注入的数据读取范围,且禁用多语句执行才是根本防御措施。

堆叠查询注入为什么比普通注入更危险
因为 mysqli_multi_query()、pg_send_query() 或 SQL Server 的分号分隔语句,允许一次执行多条 SQL 命令。攻击者输入 admin'; DROP TABLE users; -- 后,数据库可能真的执行了删除操作,而不仅是绕过登录或读取数据。
普通参数化查询(如 cursor.execute("SELECT * FROM u WHERE n = %s", [x]))天然不支持堆叠,但很多开发者误以为“用了预编译就万事大吉”,却在底层调用了支持多语句的驱动或函数,导致防线失效。
禁用多语句执行是第一道硬隔离
不是“要不要用”,而是“必须关掉”。不同语言/驱动默认行为差异极大,不能依赖文档假设安全:
-
MySQLi:初始化连接时显式关闭 ——mysqli_options($conn, MYSQLI_OPT_MULTI_STATEMENTS, false) -
psycopg2(PostgreSQL):默认禁止堆叠;但若手动调用pg_send_query()+pg_get_result()循环,则需自行校验输入中是否含分号或;字符 -
SQL Server(pyodbc):连接字符串中添加MultipleActiveResultSets=False,并避免使用executescript()类接口 - ORM 如 Django ORM、SQLModel、SQLAlchemy Core 默认不启用堆叠,但
session.execute(text("..."))若拼接用户输入,仍可能触发风险
哪些场景下堆叠注入最容易被忽略
不是登录页,而是那些“看起来不查库”的功能点:
- 导出报表功能:后端接收
format=csv和filter=xxx,拼进WHERE后又用mysqli_multi_query()执行导出+日志记录两条语句 - 管理后台的“批量更新”按钮:前端传入 JSON 数组,后端反序列化后循环拼接
UPDATE ... ; UPDATE ...并用多语句执行 - 调试接口残留:如
/api/debug?sql=SELECT%201;DELETE%20FROM%20logs,即使加了 auth,也属于高危暴露面
这类地方往往跳过了参数化流程,又没做输入长度/字符集白名单限制,是堆叠注入的温床。
仅靠过滤分号无法替代禁用多语句
攻击者可以绕过简单字符过滤:
- 用 URL 编码绕过:
%3B替代; - 用注释混淆:
admin'%0aUNION%0aSELECT%0a1,2,3%23(虽然不是堆叠,但说明过滤不可靠) - 利用数据库特性:MySQL 中
DELIMITER $$ ... END $$可绕过基础分号检测(需存储过程权限)
真正有效的做法是:在连接层关闭多语句支持 + 所有动态 SQL 构造处强制走单语句参数化路径 + 对 executescript、multi_query 等敏感 API 做代码扫描告警。堆叠注入的修复不在“怎么写得更小心”,而在“让系统根本没法执行第二条语句”。











