凌晨两点,系统测试环境里的缺陷日志突然刷出一片红色。我盯着屏幕,漏洞心跳漏了一拍——那个支付接口在并发请求下返回了重复的测试订单号。这不是系统第一次了,上周压测时就出现过,缺陷但开发说“偶发,漏洞不影响”。测试偶发?系统在金融系统里,偶发意味着客户的缺陷钱可能凭空消失两次。
漏洞测试最迷人的漏洞地方在于,它永远在跟“我以为”作对。测试你以为参数校验做全了,系统结果有人用负数金额下单,缺陷系统居然笑着通过了。漏洞你以为数据库事务隔离级别设对了,结果两个线程同时读到同一行数据,各自计算各自扣款,账就平不上了。这些坑不是写代码时能想到的,得靠测试去撞,撞得头破血流才能把隐患揪出来。
我习惯把漏洞分成三类。第一类是显性的,跑个边界值就能发现,比如输入框限制100字,你输入101字看看会不会报错。第二类是隐性的,藏在业务逻辑里,比如用户A能修改用户B的订单——这种权限漏洞最阴险,不模拟真实攻击根本露不出来。第三类是环境相关的,本机跑得好好的,一上Linux服务器就崩,多半是路径分隔符或者编码问题。
记得有一次,测试一个文件上传功能。常规的jpg、png都试了,没问题。我突发奇想,传了个带中文名的文件,结果服务器直接500。查了半天,是底层框架对非ASCII字符处理有缺陷。这种问题,你按着标准用例走一辈子也发现不了,非得有点“邪念”才行。
所以我的测试原则很简单:永远假设系统是错的,直到证明它是对的。每个接口、每个按钮、每个输入框,都当成敌人来审。同事笑我偏执,但偏执在测试这行是美德。上个月隔壁组上线前漏测了一个空指针,结果用户一打开页面就白屏,紧急回滚花了四十分钟——那四十分钟里,每秒钟都有真实用户在流失。
漏洞测试还有个反直觉的地方:自动化工具能帮你覆盖80%的常规路径,但剩下的20%靠的是人的直觉和想象力。工具不知道业务规则里“优惠券不能和满减同用”这条潜规则,它只会机械地填数据。所以我现在会在自动化跑完之后,手动构造一些“变态”场景:比如把手机号填成“123”,把邮箱填成“不存在的”,看看系统会不会优雅地拒绝,还是直接抛个堆栈跟踪给用户看。
最让我头疼的是那种“时好时坏”的缺陷。测试十次,九次通过,就一次失败。这种最难定位,往往跟内存泄漏、线程竞争或者缓存过期策略有关。上周就碰上一个,登录状态偶尔丢失,复现了三天才找到原因——是Redis的key过期时间设置成了负数,导致某些情况下缓存直接失效。这种bug就像慢性病,不发作的时候一切正常,一发作就让人抓狂。
说到底,漏洞测试不是找茬,是给系统做体检。早发现早治疗,总比上线后让用户当小白鼠强。每次测出问题,我都觉得像是捡回了一条命——不是我的命,是产品上线后可能遭遇的信任危机。这活儿不轻松,但每修掉一个缺陷,就有一批用户能少踩一个坑,值了。