游客发表
代码审计不是代码拿着工具扫一遍就完事。上个月帮朋友看一个开源商城项目,审计他信誓旦旦说用了某商业扫描器,代码结果我翻到第三十个文件就发现一处订单金额直接取自前端传参,审计连类型转换都没做。代码工具报告里干干净净。审计
这就是代码代码审计的日常——它更像刑侦,不是审计安检门。安检门只告诉你“兜里有金属”,代码至于是审计钥匙还是刀,得自己伸手进去摸。代码
很多人把代码审计和静态分析划等号。静态分析查的审计是模式:这个函数用了strcpy,标记;那个变量没过滤就拼进SQL,代码报警。这些当然有用,但真正的漏洞往往藏在业务逻辑的缝隙里。
举个例子。某次我审一个优惠券系统,代码里对券的金额做了严格校验,对使用次数也加了锁。看起来无懈可击。但下单接口里,优惠券的抵扣是在计算总价之后单独减去的,而退款接口只退了实付金额,没退券。用户下单用券、退款、再用同一张券,循环套利。扫描器不会告诉你这个,因为它“合规”。
你得把自己当成产品经理+攻击者+运维的混合体,去读代码的意图,然后找意图之外的路径。
常规做法是从Controller层往下追参数。这没错,但容易漏掉定时任务、消息队列消费者、内部RPC接口。去年一个金融项目,对外接口防护严密,结果一个跑批任务从数据库读出来的数据直接拼进了Shell命令。数据库里的数据哪来的?运营后台导入的Excel。攻击链绕了一大圈,入口在运营的U盘里。
审计的时候我会先画一张数据流图,不是那种漂亮的架构图,就是草稿纸上箭头乱飞的那种。标出哪些数据是“脏”的——用户输入、第三方回调、文件内容、甚至数据库里那些看起来可信但实际被污染的字段。然后看这些脏数据在哪些地方被“洗白”了,哪些地方直接用了。
洗白的地方最容易出问题。比如过滤函数只替换了单引号,没管反斜杠;比如用了白名单但白名单本身是从配置文件读的,而配置文件可写。
我平时也用工具,Semgrep、CodeQL、Fortify都碰。它们擅长做重复劳动:找出所有用了eval的地方,列出所有没设HttpOnly的Cookie。但工具的输出需要人判断。一个eval可能只是解析JSON,另一个eval可能直接执行了用户传的Python代码。差别巨大。
而且工具会漏。动态拼接的SQL,如果拼接逻辑跨了三个文件、用了两个中间变量,很多工具就跟丢了。这时候得靠人肉grep加脑补。
有个习惯我保持了很多年:审完一个模块,试着写一个PoC。写不出来,说明可能没审透;写出来了,说明漏洞真实存在。这个过程比看一百份报告都管用。
发现漏洞只是开始。怎么修复、修复会不会引入新问题、业务能不能接受,这些都得考虑。我见过开发为了修一个XSS,把整个富文本编辑器禁了,运营直接炸锅。也见过为了防SQL注入,把所有查询改成存储过程,结果存储过程里照样拼接。
好的审计报告会给出修复建议,但更重要的是告诉开发“为什么”。为什么这里要用参数化查询,为什么那里不能只靠前端校验。代码是人写的,漏洞也是人写的。审计到最后,审的是人的习惯和团队的流程。
所以下次有人跟你说“代码审计就是跑个工具”,你可以让他把工具报告里的误报和漏报先手工标一遍。他标不完的。
友情链接