SQL防注入唯一有效手段是强制使用参数化查询,培训仅起补漏作用;动态拼接SQL必须杜绝,ORM混用原生SQL、ORDER BY字段、表名等需白名单校验;数据库权限须按最小原则严格限制。

SQL防注入不是靠培训能解决的
安全培训对防注入效果极其有限——它无法修复代码里没用参数化查询的地方。真正起作用的是开发流程中强制使用预编译语句,以及数据库访问层的统一约束。培训只适合补漏,比如让新人知道为什么 string.Format("SELECT * FROM users WHERE id = {0}", id) 比 cmd.Parameters.AddWithValue("@id", id) 危险十倍。
所有动态拼接SQL的地方必须用参数化查询
这是唯一被验证有效的防注入手段。任何语言、任何数据库驱动,只要支持预编译,就该无条件使用。常见错误包括:在ORM中混用原生SQL但忘了参数绑定;用字符串拼接构造 WHERE 条件;把用户输入直接塞进 ORDER BY 或 GROUP BY 子句。
-
Java:禁用
Statement.execute(),只用PreparedStatement,连setString(1, input)都不能省 -
Python:避开
cursor.execute("SELECT * FROM t WHERE x = %s" % user_input),改用cursor.execute("SELECT * FROM t WHERE x = %s", [user_input]) -
.NET:不用
string.Concat("id = ", id)拼接,SqlCommand必须配Parameters.Add() -
Node.js:
pg.query("SELECT * FROM u WHERE name = '" + name + "'")是红线,得写成pg.query("SELECT * FROM u WHERE name = $1", [name])
白名单校验比黑名过滤更可靠
当参数化不可行时(如动态表名、排序字段),只能靠白名单。正则匹配 [a-zA-Z_][a-zA-Z0-9_]* 不够,因为可能绕过;而硬编码允许值列表(如 ["created_at", "updated_at", "status"])才是实际可行的方案。
-
ORDER BY后的字段名:必须从预设数组中取,不能解析用户传入的字符串 - 分表路由中的表后缀:用
switch (shardKey % 4)算出具体表名,而不是拼接"log_" + user_input - JSON字段路径(PostgreSQL的
->操作符):若需动态路径,先查配置表确认该路径是否在业务允许范围内
数据库权限要按最小原则切分
即使某处SQL没写好,攻击者也应拿不到敏感数据或执行破坏操作。一个典型问题是:应用账号拥有 DROP TABLE、EXECUTE(函数)、甚至 pg_read_server_files 权限。
- Web应用连接数据库的账号,只授予
SELECT/INSERT/UPDATE/DELETE到特定 schema 的几张表 - 禁用
publicschema 的CREATE权限,防止攻击者建临时函数 - PostgreSQL 中避免给应用账号
USAGE权限到pg_catalogschema - MySQL 8+ 应用账号不应有
FILE、PROCESS、SUPER权限
最常被忽略的一点:存储过程内部如果用了 EXECUTE IMMEDIATE 或 EXECUTE(PostgreSQL),且拼接了用户输入,那参数化就完全失效了——这种逻辑必须重写,或者加白名单校验。防注入不是加个过滤函数就能高枕无忧的事。

















