|
2017年,入漏入成某知名电商平台的洞注安全工程师在例行审计时发现,一个商品搜索框的入漏入成URL参数后面,被人悄悄追加了一段`' OR '1'='1`。洞注这个不起眼的入漏入成字符串,让数据库里所有商品信息瞬间裸奔。洞注这不是入漏入成电影情节,而是洞注SQL注入最经典的打开方式。 SQL注入的入漏入成根子,出在“拼接”二字上。洞注开发者为了图省事,入漏入成喜欢把用户输入直接拼进SQL语句。洞注比如登录功能,入漏入成代码写成`SELECT * FROM users WHERE name='$username' AND password='$password'`。洞注用户输入什么,入漏入成数据库就执行什么。攻击者输入`admin' --`,SQL语句就变成`SELECT * FROM users WHERE name='admin' --' AND password='...'`。两个连字符把后面的密码校验注释掉,登录验证形同虚设。 更隐蔽的注入藏在数字参数里。有些程序员觉得只有字符串才危险,数字总该安全吧?于是查询语句写成`SELECT * FROM products WHERE id=$id`。攻击者把id改成`1 UNION SELECT username,password FROM users`,数据库照样乖乖执行。数字类型不经过引号包裹,反而成了注入的温床。 我见过最离谱的案例,是某内部系统把排序字段直接拼进SQL。前端传个`order=price`,后端就执行`ORDER BY price`。攻击者把参数改成`(CASE WHEN (SELECT ascii(substr(password,1,1)) FROM admin)>80 THEN price ELSE id END)`,通过观察排序结果逐字符猜密码。这操作比盲注还狠,整整磨了三天才被发现。 注入的本质是信任边界崩塌。数据库把用户输入当成指令执行,是因为开发者没区分数据和代码。这就像你把家门钥匙交给快递员,还告诉他“随便放就行”——对方是送快递的还是踩点的,你根本分不清。 有些开发团队用预编译语句(PreparedStatement)堵漏洞,这法子确实管用。参数化查询把用户输入当作纯数据传给数据库,不再参与SQL语法解析。但问题在于,很多老系统改造起来成本太高,运维图省事就只给输入框加黑名单过滤。过滤单引号、过滤关键字,这些土办法就像拿创可贴治骨折,总有漏网之鱼。 真正让人头疼的是二次注入。用户注册时输入`admin'--`,数据库转义后存进去的是`admin\'--`,看着挺安全。可等管理员在后台搜索这个用户时,程序把存储的字符串取出来拼接进新SQL,转义符被数据库处理掉,注入又活了。这种潜伏式攻击能绕过大部分防护,等触发时往往已经晚了。 SQL注入能活这么多年,根本原因是开发者总在跟业务抢时间。产品急着上线,安全需求往后排,等出了事再补救。可数据库里存的是真金白银的客户数据,不是用来赌运气的筹码。每次看到有人问“怎么防止SQL注入”,我都想说:先问问自己,为什么当初写SQL时没多想想。 |