系统的漏洞
去年冬天,我在一家连锁便利店排队结账。漏洞前面的系统大爷拿了一袋盐、两盒鸡蛋,漏洞收银员扫完码说:“一共十一块三。系统”大爷掏出一张二十,漏洞收银员找零八块七。系统大爷数了数,漏洞又递过去三毛硬币,系统说凑个整。漏洞收银员愣了一下,系统重新操作,漏洞最后找给大爷九块。系统整个过程没有任何问题——直到我发现,漏洞那三毛硬币根本不在系统里。系统收银员把三毛钱直接塞进了抽屉,然后从抽屉里拿出九块钱给大爷。系统只记录了二十元收款和十一块三的应收,差额八块七。那三毛钱,是系统外的。
这就是漏洞。不是代码里的bug,不是安全漏洞,是规则和现实之间的缝隙。系统设计的时候,默认所有交易都通过它完成。但人偏偏不按套路出牌——大爷想凑整,收银员想省事,于是三毛钱绕过了系统,在物理世界里完成了交换。系统不知道,也不在乎。它只认自己的账。
我有个朋友在银行做系统维护。他说最怕的不是黑客攻击,是柜员“变通”。比如客户要转账五万,但系统规定超过五万需要主管授权。柜员就让客户先转四万九,再转一千。系统记录了两笔正常交易,主管授权环节被绕过了。你说这是漏洞吗?从系统角度看,每笔交易都合规。从风控角度看,这就是典型的拆分交易。但系统没有“合并判断”的能力,它只看单笔。
漏洞往往不在代码里,在流程的接缝处。系统设计者假设所有人都会按预设路径走,但现实中人总会找到最省力的那条路。那条路可能绕过系统,可能利用系统的盲区,可能只是把系统当成一个记账工具,真正的交易在系统外完成。
我观察到一个规律:系统越复杂,漏洞越多。不是代码质量差,是复杂度本身创造了缝隙。一个简单的收银系统只有“收款-找零”两个动作,漏洞就是那三毛钱。一个复杂的电商系统有优惠券、满减、积分、跨店活动,漏洞就是各种叠加规则之间的冲突。有人专门研究这些冲突,写攻略,发论坛。平台改一次,他们找一次。像猫鼠游戏,但老鼠从来不是恶意的——他们只是想省钱。
前阵子某生鲜电商的“退款不退货”政策被玩坏了。系统规定:生鲜商品变质,用户申请退款,无需退货。因为退货成本高于商品价值。结果有人买一箱车厘子,收到后说坏了,申请退款。系统自动通过。他吃了车厘子,还拿回了钱。平台后来改成“需上传照片”,再后来改成“需人工审核”。但漏洞已经存在了——不是系统没想到,是系统默认“用户是诚实的”。这个默认值,就是漏洞的源头。
写代码的人总想穷尽所有情况。但现实情况是穷尽不了的。你写if (user.isHonest) { ... },可现实里isHonest是个变量,今天真明天假。系统只能处理它知道的,处理不了它不知道的。而那些“不知道的”,就是漏洞的藏身之处。
所以修补漏洞,从来不只是改代码。得改流程,改规则,改人的习惯。但改人的习惯最难。收银员还是会收那三毛钱,骑手还是会卡时间,柜员还是会拆金额。只要系统和人打交道,漏洞就永远存在。区别只在于,有些漏洞被发现了,有些还没有。
那天我走出便利店,回头看了一眼。收银员正在把三毛硬币放进一个专门的格子里,和其他硬币分开。那个格子不在收银机的抽屉里,在抽屉旁边的塑料盒里。系统不知道那个盒子的存在。但我知道。
