Q88: 如何防止 SQL 注入?
核心结论
防 SQL 注入最核心的一条原则很简单:
- SQL 结构和用户输入必须分离
也就是说,查询语句的结构由程序定义,外部输入只能作为参数,而不能拼进 SQL 结构里。
剩下的输入校验、ORM、权限控制,都是补充层。
一、SQL 注入真正的根因是什么
根因通常不是“用户输入脏”,而是程序把外部输入当成 SQL 语法的一部分拼接进去。
只要结构和数据混在一起,注入风险就一直存在。
二、参数化查询是主方案,不是建议项
无论使用:
- 原生驱动
- ORM
- 查询构建器
核心都应该是参数绑定,而不是字符串拼接。
这不是风格问题,而是边界问题。
三、ORM 也不能自动保证安全
ORM 能减少手写 SQL,但不代表天然没有注入风险。
一旦出现:
- 拼接原生 SQL
- 动态表名或排序字段直拼
- 拼接 where 条件片段
风险仍然存在。
所以真正要防的是“不受控拼接”,而不是只看是不是 ORM。
四、白名单校验对动态字段尤其重要
有些场景确实需要动态控制,例如:
- 排序字段
- 排序方向
- 可选查询列
这些不能简单参数化的部分,通常应使用白名单映射,而不是把客户端输入原样拼进去。
五、权限和最小暴露面也很重要
即使有注入风险,如果数据库账号权限被严格限制,破坏面也会小很多。
更稳妥的做法通常是:
- 应用账号最小权限
- 读写账号分离
- 后台高危库隔离
这样能降低事故影响范围。
六、日志和监控里也要注意注入面
很多系统只盯主查询路径,却忽略:
- 报表查询
- 后台搜索
- 运维脚本
- 临时 SQL 工具
这些地方往往更容易因为“临时方便”而回到字符串拼接。
七、工程上更稳妥的做法
常见做法是:
- 所有查询默认参数化
- 动态 SQL 的结构部分走白名单
- 数据库账号最小权限
- 高风险后台接口单独审查
八、常见误区
1. 用 ORM 就不会有 SQL 注入
不对。只要有原生拼接,风险仍然存在。
2. 只过滤特殊字符就够了
过滤只能辅助,不能替代参数化。
3. 只有登录接口才需要防注入
后台查询、报表、搜索和脚本同样是高风险点。
参考资料
- 参数化查询、白名单动态 SQL 和最小权限实践资料
