如何对sql注入进行防护处理
去年有个做电商的何对护处朋友找我救火,说后台被人拖了整张用户表。入进我一看日志,行防攻击者就在搜索框里输入了一串 ' OR 1=1 --,何对护处后端直接把用户输入拼进了 SQL 语句,入进数据库当场变成自助餐厅。行防这种事每天都在发生,何对护处防护手段其实不复杂,入进关键是行防别偷懒。
最根本的何对护处一条:永远不要相信用户输入能直接进 SQL。我见过太多代码长这样——String sql = "SELECT * FROM users WHERE name = '" + name + "'";。入进这行代码等于把数据库钥匙插在门上还贴了张纸条说“欢迎光临”。行防参数化查询(Prepared Statement)就是何对护处来解决这个问题的。它把 SQL 语句的入进结构和用户数据彻底分开,数据库先编译好语句骨架,行防再把数据当纯值塞进去,哪怕你输入一万个单引号也只会被当成字符串内容,不会改变语句逻辑。Java 里用 PreparedStatement,Python 用 cursor.execute("SELECT * FROM users WHERE name = %s", (name,)),PHP 用 PDO 的 prepare 加 bindParam。就这么简单,但很多人图省事还在用字符串拼接。
有些场景参数化查询不好使,比如用户要动态指定排序字段或者表名。这时候别硬拼,用白名单。只允许 name、created_at 这几个固定值,其他一律拒绝。我见过一个项目允许前端传 orderBy 参数直接拼进 ORDER BY 后面,结果攻击者用 ORDER BY (SELECT ...) 把数据一点点带出来。白名单能解决这种问题,代码写起来也就多几行判断。
输入验证不是万能的,但该做还得做。比如用户 ID 应该是纯数字,那就用正则或者类型转换强制成整数。有个细节:别只在前端验证,前端那点 JavaScript 攻击者随手就能绕过。后端收到请求第一件事就是检查参数格式,不符合直接返回 400。这层过滤能挡掉大量低级扫描器,虽然挡不住精心构造的注入,但至少让攻击成本高一点。
数据库权限也得收着点。很多应用连数据库用的是 root 或者 sa,这等于给攻击者递了把万能钥匙。Web 应用连接数据库的账号只给必要的权限——查用户表就只给 SELECT,别给 DROP、FILE、EXECUTE 这些。我习惯给每个应用单独建账号,权限按最小化原则来。万一真被注入了,攻击者也只能在那一亩三分地里折腾,拖不走整个库。
错误信息别直接吐给用户。有些框架默认把 SQL 异常堆栈返回到前端,攻击者看到 You have an error in your SQL syntax near '...' 就知道注入点在哪,还能根据报错内容猜表结构。生产环境统一返回“系统繁忙”或者自定义错误码,详细日志写到服务器本地就行。这个改动很小,但能大幅增加盲注的难度。
ORM 框架用好了能省不少心,但别以为用了 Hibernate 或者 MyBatis 就万事大吉。MyBatis 里 ${ } 和 #{ } 的区别就是生死线——${ } 直接拼接,#{ } 走预编译。我见过有人写 ORDER BY ${ sortField} 还觉得没问题,结果被注得千疮百孔。用 ORM 也得知道它底层在干什么,不然照样踩坑。
最后说个容易被忽略的点:定期用 sqlmap 扫自己的接口。别等别人来扫。找个测试环境,把登录、搜索、分页这些参数都跑一遍,看看有没有漏网的。我一般还会在代码审查清单里加一条:任何出现字符串拼接 SQL 的地方都必须有充分理由,否则打回重写。防护 SQL 注入没有银弹,但参数化查询加上最小权限,已经能挡住 95% 以上的攻击。剩下的靠白名单和输入验证兜底。别嫌麻烦,数据泄露一次,麻烦就是一百倍。
```(责任编辑:热点)














