SQL注入防御必须全程使用参数化查询,禁用字符串拼接;白名单校验优于黑名过滤;ORM不自动免疫,需避免raw/extra/${}等非安全用法;关键语法结构须预定义枚举。

SQL注入防御的基准检查表\_构建合规性的安全操作规范
所有动态拼接 SQL 的地方,只要没用参数化查询,就默认存在高危风险——不是“可能被利用”,而是“只要攻击者知道接口或表结构,就能利用”。
必须用 PreparedStatement(Java)或等效参数化机制
手写字符串拼接 SELECT * FROM users WHERE id = ' + userInput 是最常见也最危险的写法。数据库不会区分哪部分是逻辑、哪部分是数据,它只执行最终语句。
- Java 必须用
PreparedStatement,禁用Statement执行含用户输入的 SQL - Python 用
cursor.execute("SELECT * FROM t WHERE x = %s", [value]),禁用f"WHERE x = '{value}'" - PHP 用
PDO::prepare()或mysqli_prepare(),不用mysqli_query("...$var...") - Node.js 用
pool.query("SELECT * FROM t WHERE id = ?", [id]),禁用模板字面量直接插值
白名单校验比黑名过滤更可靠
试图用正则删掉 '、;、UNION 等字符,本质是打补丁式防御——绕过方式太多,比如大小写混写、编码变形、注释分隔等。
- 排序字段名(如
ORDER BY name)不能靠过滤,应限定为枚举:if (sortField not in ['name', 'created_at', 'status']) raise ValueError - 表名、列名、LIMIT 数值等语法结构级内容,一律不接受自由输入,必须映射到预设键值对
-
IN子句多个参数?别拼字符串,用占位符数组:WHERE id IN (?, ?, ?),数量动态生成占位符
ORM 并不自动免疫 SQL 注入
很多团队误以为用了 Django ORM 或 SQLAlchemy 就安全了,但绕过方式很直接:调用 extra()、raw()、text()、字符串格式化拼接 filter() 参数时传入 f-string,都会破防。
- Django:禁用
Model.objects.extra(where=["name = '%s'" % user_input]);用Q()或命名参数extra(where=["name = %s"], params=[user_input]) - SQLAlchemy:避免
session.execute(text("SELECT * FROM t WHERE x = '" + input + "'"));改用text("SELECT * FROM t WHERE x = :val").bindparams(val=input) - MyBatis:禁用
${xxx},只用#{xxx};${}是字符串替换,#{}才走预编译
真正难的不是写对一行参数化代码,而是确保整个调用链上没有一个环节偷偷把变量塞进了 SQL 字符串里——尤其在封装工具函数、日志埋点、动态条件组装时,最容易漏掉那一个 + 号。

















