去年冬天,搭建漏洞我在一个众测平台上看到某款智能门锁的搭建漏洞漏洞报告,提交者只用了三行Python代码就绕过了指纹验证。搭建漏洞厂商的搭建漏洞回应是“物理接触不算漏洞”。这事让我想了很久——我们到底在搭建什么?搭建漏洞是安全防线,还是搭建漏洞漏洞本身?
先说个更早的例子。2017年,搭建漏洞某国产摄像头品牌被曝出固件里硬编码了一个telnet后门,搭建漏洞账号root,搭建漏洞密码是搭建漏洞“vizxv”。这个密码在暗网上流传了至少三年。搭建漏洞厂商后来发补丁了,搭建漏洞但补丁只改了密码,搭建漏洞没改架构。搭建漏洞攻击面还在,搭建漏洞只是换了个门牌号。
漏洞不是“被发现的”,是“被搭建出来的”。每一行赶工期的代码、每一个“先上线再说”的决策、每一次“这个接口内部用不用鉴权”的侥幸,都是在给未来的自己挖坑。我见过一个后台管理系统,登录接口做了图形验证码,但修改用户密码的接口只校验了cookie里的user_id。改一下user_id就能改别人的密码。开发者的解释是“前端已经限制了”。前端限制?抓包工具笑出了声。
这种逻辑漏洞比内存溢出更常见,也更难防。缓冲区溢出至少还有编译器警告,逻辑漏洞连个报错都没有。你写`if (user.isAdmin)`,但忘了在数据库查询里加`and role='admin'`,代码跑起来一切正常,直到有人把请求里的`role`参数改成`admin`。
我跟踪过一个小型电商站的渗透测试过程。测试者发现搜索框有SQL注入,但过滤了`union`和`select`。于是他用了`/*!50000union*/`——MySQL的内联注释,照样跑通。厂商的WAF规则写得像筛子,只拦关键词,不拦语义。这种“搭建”方式很典型:先有漏洞,再补规则,规则永远追着漏洞跑。
更隐蔽的是供应链漏洞。你用了某个npm包,包作者两年前离职了,代码里有个`eval`。没人看,没人改。某天有人提交了个PR,加了个“性能优化”,实际上在`eval`里插了远程加载。你`npm install`的时候,后门就进来了。这不是科幻,event-stream事件才过去几年?
搭建漏洞的人往往不觉得自己在搭建漏洞。他们觉得在搭建功能、搭建效率、搭建“先跑起来再说”。安全是后来才想的事,而“后来”通常意味着“出事之后”。我见过一个团队,上线前三天才做安全测试,测试报告里写了17个高危,产品经理说“这版先上,下版修”。下版?下版有新功能要赶,漏洞还在那儿。
那怎么办?我的看法很直接:别把安全当阶段,当习惯。写接口的时候多想一步“如果我是攻击者会怎么改参数”,比事后买WAF有用。代码评审的时候多问一句“这个输入校验能绕过吗”,比渗透测试报告便宜。至于那些“物理接触不算漏洞”的厂商——他们的门锁我肯定不会买。
漏洞从来不是天灾,是人祸。每一行省事的代码,都是给未来埋的雷。你搭得越快,雷埋得越密。等到爆的那天,别怪攻击者太聪明,先想想自己当初为什么没多写那行校验。