如何对sql注入进行防护
2011年,何对护索尼PlayStation网络被拖库,入进7700万用户数据泄露,行防事后调查确认是何对护SQL注入漏洞惹的祸。那会儿我刚开始写代码,入进看到这条新闻只觉得“这玩意儿真的行防还存在?”十几年过去,OWASP Top 10里SQL注入依然稳坐头把交椅。何对护别笑,入进直到今天,行防我审计过的何对护代码里,每十套系统至少还有一套能直接拼SQL。入进 SQL注入的行防根子就一句话:把用户输入当成了代码执行。你本来只想让用户传个用户名,何对护结果他传了“ 拿Java的JDBC举例: 但有人会杠:“我用了参数化怎么还是被注了?”那多半是代码里某些地方图省事,把动态表名、列名或者排序字段直接拼进去了。比如用户传个 第二道防线是输入校验。别迷信“过滤单引号”这种土办法,编码绕过分分钟让你怀疑人生。真正做的是类型和格式校验:数字就强制 第三道防线是数据库权限最小化。很多系统用root账号连库,注入成功直接拖全表。正确做法是:应用账号只给 还有个容易被忽略的点:报错信息别回显给用户。数据库报错里经常带表名、字段名,甚至拼接的SQL片段,这些是攻击者的地图。生产环境一律抛通用错误页,详细的异常日志写到服务端文件里就行。我见过某系统直接把 最后说个现实问题:老系统改造成本高,没法全量参数化怎么办?那就做转义兜底。数据库驱动自带的 写代码的时候多问自己一句:如果用户传的是' OR 1=1 --”,入进你的行防SQL就变成了“查所有用户”。所以最根本的防护思路不是去过滤什么,而是让数据和代码彻底分开——参数化查询就是干这个的。PreparedStatement ps = conn.prepareStatement("SELECT * FROM users WHERE name = ?");ps.setString(1, userName);
问号占位,参数单独传,数据库端会把它当纯字符串处理,哪怕里面全是“' OR 1=1”也翻不起浪。Python的cursor.execute("...WHERE name = %s", (name,))同理,PHP的PDO绑定参数也一样。这是第一道防线,也是最重要的一道,几乎能挡掉99%的注入。order=id;DROP TABLE users,你拼成ORDER BY id;DROP TABLE users——参数化救不了这种场景,因为表名和列名不能当参数绑。这时候只能靠白名单校验:写死一个允许的字段列表,用户传什么你都先比对一遍,不在列表里直接拒绝。我见过太多人在这种边角料上栽跟头,还一脸无辜。is_numeric,邮箱就按正则跑一遍,字符串长度限制到合理范围。这不是为了防注入本身,而是减少攻击面——你让用户传个1000字的“用户名”,本身就透着不正常。SELECT、INSERT、UPDATE、DELETE,绝不给DROP、ALTER、FILE这种高危权限。万一真被注入了,攻击者能做的也有限——他顶多读数据,删不了库。别小看这条,2017年Equifax泄露1.4亿条数据,事后调查发现数据库账号权限大得离谱,连备份文件都能读。SQLException堆栈打到前端,等于把数据库结构画给人家看。escapeString函数用起来,虽然不如参数化干净,但至少让注入难度翻几倍。记住这只能算过渡方案,别当成长期依赖——安全这东西,最怕的就是“差不多得了”。'; DROP TABLE users; --,我的SQL会变成什么?想清楚这个,比背一百条安全规范都管用。
-
上一篇
-
下一篇