2023年秋天,一个叫“快省”的购物App被推上了风口浪尖。起因是一位用户发现,自己在App里领的“满100减50”优惠券,竟然能通过反复修改收货地址来重复使用。他把过程录屏发到网上,三天内,这条视频播放量破了千万。快省官方紧急下架了优惠券功能,但损失已经造成——有人用脚本批量操作,一夜之间薅走了价值十几万的商品。
这不是什么高深的技术漏洞。说白了,就是开发人员忘了在服务端做校验。用户点“领取”按钮时,前端会发一个请求,服务器返回“领取成功”并标记该用户已领。但如果你在提交订单的瞬间,把收货地址改一下,前端会重新触发一次领取请求,服务器居然又发了一张券。同一个账户,同一个活动,领了两次。逻辑上根本说不通,可它就这么发生了。
我后来跟一个做安全测试的朋友聊起这事。他笑了笑,说这种漏洞在他们眼里叫“逻辑漏洞”,比SQL注入、缓冲区溢出那些老牌漏洞更常见,也更难防。为什么难防?因为扫描器扫不出来。你写个自动化工具去跑,它只能测出“这里有个输入框没过滤特殊字符”,但测不出“改地址能重复领券”。逻辑漏洞依赖的是对业务的理解,你得知道这个App是怎么运转的,才能找到那条歪路。
他给我举了个例子。某银行App的转账功能,输入收款账号后,页面会显示对方姓名。正常流程是:输入账号→请求服务器→返回姓名→确认转账。但有人发现,如果在返回姓名之前,快速把账号改成自己的,再点确认,钱会转给自己,但系统记录里显示的收款人还是原来那个。这叫什么?这叫“条件竞争”。开发人员以为用户会老老实实等着页面加载完,可总有人不老实。
这类漏洞的根源,往往不是技术不行,而是思维偷懒。开发的时候,脑子里想的是“用户会按我设计的流程走”,可现实里,用户会点返回、会断网、会改参数、会开多个窗口。你少写一个“if”,就可能被人钻出几万块的损失。快省那个优惠券漏洞,修复方案其实就一行代码:在服务端加个锁,判断该用户是否已领过。但这一行代码,上线前没人想到。
更麻烦的是,很多公司不把逻辑漏洞当回事。我见过一个创业团队,App上线前只做了功能测试,安全测试完全没做。我问他们为什么,负责人说:“我们又不是银行,谁闲着没事攻击我们?”结果上线第二周,就有人用批量注册的账号刷走了所有新人红包。他后来跟我说,那晚他盯着后台数据,看着红包一个个被领走,却不知道该封哪个账号——因为所有账号看起来都“正常”。
说实话,App漏洞这事,防是防不完的。你修了优惠券,可能又冒出个积分兑换的漏洞;你堵了转账的条件竞争,可能又有人发现退款流程能套现。但至少,开发的时候得多想一步:如果我是攻击者,我会怎么绕过这个流程?这个“多想一步”,比任何安全工具都管用。
快省后来更新了版本,优惠券漏洞没了。但我在应用商店的评论区看到,有人留言:“修复了?那我换别的App薅。”你看,漏洞可以修,但人性里的那点贪便宜和好奇心,永远修不完。