您现在的位置是:烟消火灭网 > 时尚

逻辑缺陷漏洞修复手段

烟消火灭网2026-10-17 07:42:10【时尚】5人已围观

简介去年冬天,我帮朋友排查一个订单系统的Bug。用户下单后,偶尔会收到两笔扣款,但订单只生成一条。查了两天,代码逻辑没毛病,数据库也没重复写入。最后发现是前端在提交按钮上绑了两次点击事件——一次是框架自动

去年冬天,逻辑漏洞我帮朋友排查一个订单系统的缺陷Bug。用户下单后,修复偶尔会收到两笔扣款,手段但订单只生成一条。逻辑漏洞查了两天,缺陷代码逻辑没毛病,修复数据库也没重复写入。手段最后发现是逻辑漏洞前端在提交按钮上绑了两次点击事件——一次是框架自动绑的,一次是缺陷开发手动加的。网络稍慢,修复用户多点一下,手段两个请求同时到达后端。逻辑漏洞后端判断“是缺陷否已处理”的逻辑用的是“查一下有没有”,而不是修复“锁一下再查”。两个请求几乎同时查到“没有”,于是都往下走。

这不是代码写错了,是逻辑上留了一个时间窗口。修复手段很简单:把“查”和“写”放进同一个原子操作里,或者加分布式锁。但找到这个漏洞花了很久,因为逻辑缺陷不像语法错误那样会报错,它只在特定时序下露头。

逻辑漏洞的根:假设太干净

写代码的人脑子里通常有一条干净的主线:用户点一次,请求来一次,处理一次,返回一次。现实不是这样。网络会重发,用户会连点,定时任务会重叠,消息队列会重复投递。逻辑漏洞几乎都藏在这些“意外”里。

我见过一个权限校验的逻辑:先查用户角色,再查资源归属,两个都通过就放行。问题出在“查角色”和“查归属”之间,用户的角色被管理员改了。这个窗口可能只有几十毫秒,但足够让一个已经被降权的用户拿到敏感数据。修复方式不是加判断,而是把两次查询合并成一次带条件的查询,让数据库来保证原子性。

修复不是打补丁,是改判断条件

很多人修逻辑漏洞的习惯是“再加一个if”。比如发现重复提交,就在入口加一个“如果已经提交过就返回”。这能挡住一部分,但挡不住并发。真正有效的修复是改变判断的粒度:把“是否存在”改成“是否可插入”,把“先查后改”改成“改的时候带条件”。

举个例子。一个积分兑换逻辑,先查用户积分够不够,再扣减。并发下,两个请求都查到积分够,都扣,积分变成负数。修复方案是扣减时加条件:UPDATE user SET points = points - 100 WHERE id = ? AND points >= 100。看影响行数,0就是失败。一条SQL解决,不需要事务,不需要锁。

有些漏洞修不了,只能绕

不是所有逻辑缺陷都能在代码层修干净。比如依赖第三方接口的回调,对方可能重复通知,也可能不通知。你能做的是让处理逻辑幂等——同一个通知处理多次,结果一样。幂等不是靠“查一下有没有处理过”,而是靠唯一约束或者状态机。数据库的唯一索引是最可靠的防线,比任何应用层判断都稳。

还有一种情况:业务逻辑本身就有歧义。比如“用户注销后,他的订单归谁”。这种不是技术漏洞,是产品定义缺失。修复手段是先把业务规则定死,再写代码。技术不能替产品做决定。

我的习惯:先画时序,再写断言

现在拿到一个涉及状态变更的需求,我会先画时序图——不是画正常流程,是画异常流程:请求重发、回调重复、任务重叠、用户中途改状态。画完再写代码,每个关键判断点加一个断言或日志。上线后如果断言触发,说明逻辑假设错了,比用户投诉早一步发现。

逻辑漏洞修复没有银弹。它要求写代码的人承认一件事:你脑子里的那条干净主线,在真实世界里从来不存在。

很赞哦!(13)