您现在的位置是:烟消火灭网 > 休闲
sql注入漏洞修复方法
烟消火灭网2026-10-17 07:40:24【休闲】0人已围观
简介去年帮一个做电商的朋友看代码,发现他后台登录页的查询语句是这么写的:SELECT * FROM users WHERE username = '" + username + "' AND passwo
去年帮一个做电商的入漏朋友看代码,发现他后台登录页的洞修查询语句是这么写的:SELECT * FROM users WHERE username = '" + username + "' AND password = '" + password + "'。我当时随手在用户名框里敲了 ' OR '1'='1,复方法密码随便填,入漏直接进了后台。洞修他脸都白了。复方法这种拼接字符串的入漏写法,就是洞修SQL注入的根。
修复的复方法核心只有一句话:让用户输入的数据永远只是数据,不能被数据库当成命令来解析。入漏听起来简单,洞修做起来有几种路子,复方法各有各的入漏坑。
参数化查询是洞修首选,别偷懒
几乎所有主流语言和数据库驱动都支持预编译语句。复方法以Java的JDBC为例,把上面那段改成:
String sql = "SELECT * FROM users WHERE username = ? AND password = ?"; PreparedStatement ps = connection.prepareStatement(sql); ps.setString(1, username); ps.setString(2, password); ResultSet rs = ps.executeQuery(); 这里的 ? 是占位符,数据库会先把SQL语句的结构编译好,再把用户输入的值填进去。哪怕用户输入的是 ' OR '1'='1,数据库也只把它当成一个普通的字符串来匹配,不会执行里面的逻辑。这是最干净的做法,性能也更好,因为预编译的语句可以被数据库缓存复用。
Python的sqlite3、PHP的PDO、Node.js的mysql2,用法大同小异。关键是要养成习惯:只要SQL语句里出现变量,就用占位符。没有例外。
存储过程不等于安全
有人觉得用了存储过程就万事大吉。不一定。如果存储过程内部还是用字符串拼接来构造动态SQL,照样能被注入。比如MySQL里用 CONCAT 拼SQL再 PREPARE,如果拼接的内容来自外部参数,风险依旧存在。存储过程本身不是护身符,关键还是看里面怎么处理输入。
输入验证是辅助,不是主力
我见过一些项目用正则过滤关键字,比如把 SELECT、UNION、-- 这些词拉黑。这种做法的毛病很明显:绕过方式太多了。大小写混写、注释穿插、编码变形,随便一种就能突破。而且正常的业务数据里也可能包含这些词,比如一个商品名叫“UNION牌扳手”,直接被误杀。
输入验证可以做,但定位应该是辅助层。比如用户ID应该是纯数字,那就校验必须是数字;邮箱格式不对就拒绝。这能挡掉一部分低级攻击,也能提升数据质量。但别指望它来防注入,主力永远是参数化查询。
最小权限原则,别让Web账号当上帝
很多项目连数据库用的是root或者sa,一旦被注入,攻击者能直接读整个库、写文件、甚至执行系统命令。正确的做法是给Web应用单独建一个数据库账号,只授予它需要的权限。比如只用到 SELECT、INSERT、UPDATE,那就只给这三个,DROP、FILE、EXECUTE 这些统统不给。这样即使有注入点,攻击者能造成的破坏也被限制在很小的范围内。
我自己的习惯是,每个应用一个独立账号,权限按表来分,能只读的绝不写。麻烦一点,但睡得踏实。
ORM不是万能,但能帮你挡掉大部分
用Hibernate、MyBatis、Sequelize这类ORM框架,默认情况下它们生成的SQL就是参数化的。但要注意,有些ORM提供了“原生SQL查询”的接口,如果在那里手动拼接字符串,等于自己把门打开。用ORM就老老实实用它的查询API,别图省事去拼SQL。
最后说一个真实案例
某次代码审计,发现一个查询接口的参数看着是整数,后端也确实做了类型转换,但转换用的是 Integer.parseInt(),异常被catch之后没做任何处理,继续用原始字符串去查库。结果传个 1 OR 1=1 进去,parseInt抛异常,catch块里啥也没干,原始字符串照样进了SQL。这种“看起来做了防护”的代码最危险,因为它会让人放松警惕。
修复SQL注入没有银弹,但有一条铁律:永远不要相信用户输入,永远不要拼接SQL。做到这两点,基本上就稳了。
很赞哦!(4832)







