本文目录导读:

- 核心防线:使用参数化查询(Prepared Statements)
- 关键补充:严格限制数据库用户权限(最小权限原则)
- 架构层防御:使用ORM框架并禁用多语句执行
- 输入验证与过滤(辅助手段,不可替代核心防御)
- 高级防御:Web应用防火墙(WAF)与数据库防火墙
- 错误处理与信息隐藏
- 特殊场景:存储过程
- 防御优先级金字塔
堆叠注入(Stacked Queries Injection)是一种比普通SQL注入更危险的攻击方式,因为它允许攻击者在一个数据库连接中执行多条SQL语句,规避堆叠注入风险需要从代码层面、架构层面和运维层面综合防御。
以下是具体的规避策略,按优先级从高到低排列:
核心防线:使用参数化查询(Prepared Statements)
这是最有效、最根本的防御手段,参数化查询将SQL语句的结构与数据参数分离,数据库引擎会严格区分代码和数据,无论用户输入什么内容,都只会被当作参数值,而不会破坏SQL语句结构。
-
错误做法:使用字符串拼接SQL语句。
# 高危 sql = "SELECT * FROM users WHERE id = '" + user_input + "'"
-
正确做法:使用占位符。
# Python (使用MySQL驱动) cursor.execute("SELECT * FROM users WHERE id = %s", (user_input,)) # Java (JDBC) PreparedStatement ps = conn.prepareStatement("SELECT * FROM users WHERE id = ?"); ps.setString(1, userInput); -
为什么有效:堆叠注入依赖于在一条语句结束后执行分号()并开始新语句,参数化查询不会解析参数中的分号,因此
1; DROP TABLE users只会被当作字面量字符串去匹配id字段,而不会执行删除表操作。
关键补充:严格限制数据库用户权限(最小权限原则)
即使参数化查询因某种原因失效(如极少数不支持该功能的旧框架),权限控制是最后一道防线。
- 禁止使用高权限账号:避免在Web应用连接数据库时使用
root或sa或DBA账号。 - 按需授权:为应用程序的数据库连接创建专用账号,只授予其执行单条查询所需的权限,并禁用可能被堆叠注入利用的危险操作。
- 禁止:
CREATE、DROP、ALTER、INSERT、UPDATE、DELETE(如果应用只读)。 - 禁止:
EXECUTE(执行存储过程,尤其在MSSQL中)。 - 仅允许:
SELECT(如果应用为只读)。
- 禁止:
- 关键点:堆叠注入需要执行第二条语句(如
DROP),如果应用账号没有删除表的权限,攻击者即使注入成功,数据库也会返回权限错误,从而阻止破坏。
架构层防御:使用ORM框架并禁用多语句执行
大多数现代ORM(如Hibernate, Entity Framework, MyBatis, SQLAlchemy)默认使用参数化查询,但需注意配置。
- 禁用多语句:许多数据库驱动允许在单次
execute()中执行多条语句(即堆叠),但该功能应被明确关闭。- MySQL:在连接字符串中设置
allowMultiQueries=false(默认应如此,需显式检查)。jdbc:mysql://localhost:3306/db?allowMultiQueries=false - SQL Server:默认支持堆叠,需确保应用代码不使用
executenonquery同时执行多条SQL。
- MySQL:在连接字符串中设置
- 使用ORM的预编译特性:如MyBatis的语法(而非),Hibernate的
setParameter。
输入验证与过滤(辅助手段,不可替代核心防御)
虽然不能完全依赖过滤,但可以作为深度防御的一部分。
- 白名单验证:如果输入必须是整数(如ID)、字母或固定格式(如UUID),则严格使用正则表达式验证格式。
^\d+$仅允许数字。
- 黑名单过滤(不推荐为主):过滤特殊字符(分号、注释符、、
xp_cmdshell、WAITFOR DELAY等)。注意:编码绕过(URL编码、Unicode编码)很容易突破黑名单。
高级防御:Web应用防火墙(WAF)与数据库防火墙
- WAF:部署ModSecurity等WAF,配置规则拦截常见的SQL注入负载(如
; DROP、; EXEC),WAF可以检测到堆叠注入的典型特征(分号后紧跟SQL关键字)。 - 数据库防火墙:专业数据库审计和防火墙产品可以基于SQL语法树(AST)进行分析,识别并阻断堆叠语句的执行。
错误处理与信息隐藏
- 关闭详细错误:不要将数据库原始错误(如
You have an error in your SQL syntax... near 'DROP TABLE')直接返回给用户,这会给攻击者提供非常有用的信息(如数据库类型、语句结构)。 - 使用统一错误页:生产环境应显示模糊的错误信息(如“处理请求时出现异常”),并将详细错误记录到服务器日志。
特殊场景:存储过程
- 避免动态SQL:不要将用户输入直接拼接到存储过程内部的SQL字符串中。
- 使用参数化:存储过程内部也应使用参数化查询。
防御优先级金字塔
- 必选(核心):参数化查询(Prepared Statements)。做对这一步,99%的堆叠注入都会被阻断。
- 必选(兜底):限制数据库账号权限(无DROP/INSERT权限)。
- 强烈建议:禁用多语句执行(
allowMultiQueries=false)。 - 建议:使用WAF + 输入白名单验证。
- 不依赖:纯黑名单过滤。
一句话总结:永远不要信任用户的输入,永远使用参数化查询,永远不要给应用数据库账号过高的权限。 只要做到这三点,堆叠注入的风险基本可以被清零。