参数化查询是SQL注入防护的核心,必须用于WHERE、INSERT、UPDATE等值部分;无法参数化的表名、列名、ORDER BY等须用白名单校验;ORM的raw()、extra()等原生SQL接口仍需严格参数化。

不需要,而且盲目过滤反而可能掩盖真正的问题。SQL注入防护的关键不是“对所有输入字段做过滤”,而是“在哪用、怎么用、哪些根本不能拼接”。
参数化查询能覆盖的字段,别碰过滤
只要字段参与 WHERE、INSERT、UPDATE SET 中的值部分,且数据库驱动支持参数占位符(如 ?、$1、%s),就该直接走参数化,而不是先过滤再拼接。
-
mysql_real_escape_string()已废弃,mysqli_real_escape_string()仅在极少数不支持预编译的老环境里临时兜底,绝不能当成主力 - 用
filter_var()验证邮箱或整数,是业务逻辑需要,不是防注入——哪怕邮箱格式合法,' OR 1=1 --也能塞进email字段里,只要后端还用字符串拼接 - Python 的
cursor.execute("SELECT * FROM user WHERE id = %s", [user_id])中,user_id是整数还是字符串、带不带单引号,都不影响安全;但换成f"WHERE id = {user_id}",立刻失效
无法参数化的部分,白名单比过滤更可靠
表名、列名、ORDER BY 字段、LIMIT 偏移量这些语法结构位,数据库不接受参数占位符。试图用正则“过滤掉 UNION”或“替换分号”毫无意义——攻击者用 %00、/**/、大小写混写就能绕过。
-
ORDER BY后的字段:只允许从["created_at", "status", "score"]中取值,匹配不上直接 400,不尝试“修正”或“默认值” - 动态表名:前端传
type=orders,后端查配置表映射到真实表名customer_orders_v2,而不是直接拼"SELECT * FROM " + user_input -
LIMIT数值:转成整数后做范围检查,Math.min(Math.max(parseInt(n), 1), 100),别转成字符串再插进 SQL
ORM 并不自动免疫,raw() 和 extra() 是高危接口
用了 Django ORM 或 SQLAlchemy 不代表安全。只要调用了原生 SQL 接口,风险就回归原始状态。
- Django 中
Model.objects.extra(where=["name = '%s'" % user_input])是典型漏洞,应改用filter(name=user_input) - SQLAlchemy 中
session.execute(text("SELECT * FROM users WHERE role = '" + role + "'"))错得离谱,必须写成text("WHERE role = :role").bindparams(role=role) - 全局搜索代码里的
.execute(、.raw(、.extra(,逐个确认是否用了参数占位符,没用的立即重构
最麻烦的是:SQL 注入往往不报错、不记录、不告警。它安静地把数据导出或删掉,直到审计时才发现。所以重点不在“我加了过滤”,而在“我有没有让任何用户输入,以字符串形式出现在 SQL 模板里”。

















