游客发表
去年帮一个做电商的防范方法朋友看服务器日志,发现有人用 `' OR '1'='1` 这种老掉牙的防范方法 payload 在登录接口上刷了几万次。居然还真让他试出两个弱密码账户。防范方法SQL注入这东西,防范方法二十多年了,防范方法依然活着,防范方法而且活得挺好。防范方法
最有效的防范方法防范手段其实就一条:别把用户输入拼进 SQL 语句里。听起来像废话,防范方法但我见过太多代码是防范方法这么写的——
String sql = "SELECT * FROM users WHERE name = '" + userName + "'"; 只要 userName 传进来一个 `admin'--`,后面的防范方法密码校验就被注释掉了。这种拼接方式在 PHP、防范方法Java、防范方法Python 里都常见,防范方法尤其是防范方法老项目或者赶工期的外包代码。
解决办法是用参数化查询。Java 里叫 PreparedStatement,Python 里是 `cursor.execute("SELECT * FROM users WHERE name = %s", (userName,))`,PHP 用 PDO 的 bindParam。原理很简单:SQL 语句的结构先被数据库编译好,用户输入的值只作为数据传入,不参与语法解析。这样即使输入里带引号、分号、注释符,也只会被当成普通字符串。
但参数化查询有个坑——它只能用于值的位置。表名、列名、ORDER BY 后面的字段名,这些不能参数化。有人图省事,直接把排序字段拼进去:
ORDER BY " + sortColumn 攻击者传个 `id; DROP TABLE users--` 就完蛋。这种场景得用白名单校验,比如只允许 `id`、`name`、`created_at` 这几个值,其他一律拒绝。
另一个常见误区是转义函数。很多人觉得用 `addslashes()` 或者 `mysql_real_escape_string()` 就够了。单引号是转义了,但如果是数字型注入呢?`WHERE id = " + userId`,传个 `1 OR 1=1`,根本不需要引号。转义函数对数字型注入完全无效。而且不同数据库的转义规则不一样,MySQL 的转义放到 SQL Server 上可能出问题。
还有一种情况是 ORM 框架。Hibernate、MyBatis 这些确实能挡住大部分注入,但前提是你别自己作。MyBatis 里用 `${ }` 而不是 `#{ }`,等于把参数化查询的防护直接扔掉。我见过一个项目,所有查询都用 `${ }`,问为什么,答曰“方便调试”。方便是方便了,黑客也方便了。
存储过程呢?理论上安全,但如果存储过程内部动态拼接 SQL,照样中招。比如 `EXEC('SELECT * FROM users WHERE name = ''' + @name + '''')`,这跟直接拼没区别。
最后说一个容易被忽略的点:数据库权限。很多应用连接数据库用的是 root 或者 sa,一旦注入成功,攻击者可以直接读系统表、写文件、甚至执行命令。给应用分配一个最小权限的账号——只能 SELECT、INSERT、UPDATE、DELETE 特定几张表,不能 DROP、不能 FILE、不能 INTO OUTFILE。这样即使有注入点,损失也可控。
我自己的习惯是,写完任何带数据库交互的代码,先搜一遍有没有字符串拼接 SQL 的地方。有就改,改不了就加白名单。这活儿不复杂,但得较真。
友情链接