详解并发漏洞
字数 2257 2025-10-18 11:17:50
并发逻辑漏洞详解教学文档
一、 核心概念:什么是并发漏洞?
并发漏洞,也称为竞态条件漏洞,是一种在高并发场景下,由于程序对共享资源(如余额、库存、领取状态)的访问顺序处理不当,导致实际执行结果与开发者预期不符的安全问题。
核心原因:多线程或多进程环境中,开发者在处理共享数据时,缺乏必要的同步控制机制(如加锁),或者同步逻辑存在缺陷,使得多个请求能够几乎同时通过关键校验点。
二、 并发漏洞的常见业务场景
该漏洞通常出现在存在“数量限制”或“状态校验”的业务逻辑中,例如:
- 签到/抽奖:限制用户每日只能签到或抽奖一次。
- 优惠券/积分领取:限制每个用户只能领取一张优惠券或固定数额的积分。
- 购买限制:限制用户购买特价商品的数量。
- 支付漏洞:多个并发支付请求可能只进行一次实际的余额扣减,导致用户以极低成本完成多次交易。
- 商品库存/余额更新:并发下单可能导致超卖(库存减为负数)或余额扣减不正确。
三、 漏洞产生的根本原理:并发 vs. 串行
要理解漏洞,必须区分两种程序执行模式:
- 串行操作:请求依次处理。必须等待上一个请求完全执行完毕(包括数据写入数据库)后,才会开始处理下一个请求。这是开发者通常预期的逻辑。
- 并发操作:多个请求交替或同时执行。现代Web服务器为提升性能,会采用多线程技术同时处理多个请求。
漏洞产生过程(以“并发领取优惠券”为例):
一个正常的领取流程涉及三张表:用户表、优惠券表、用户-优惠券关系表。理想中的串行流程如下:
- 用户A请求领取。
- 服务器查询关系表,检查用户A是否已领取。
- 如果未领取,则向关系表中插入一条领取记录。
- 返回领取成功。
然而,在并发环境下,如果没有同步控制,流程可能如下:
- 请求A 到达服务器,执行查询,发现用户未领取。
- 在请求A 向数据库插入记录之前,请求B 也到达服务器,并执行查询。此时由于请求A的插入操作尚未完成,请求B的查询结果同样是“用户未领取”。
- 请求A 和 请求B 都基于“未领取”的判断,先后向数据库插入了领取记录。
- 结果:用户成功领取了两张优惠券,绕过了限制。
根本原因:在“查询”和“更新”(写库)两个操作之间存在一个时间窗口。并发请求在这个时间窗口内同时通过了校验。
四、 并发漏洞的测试方法
测试并发漏洞的核心是:在极短时间内,向目标接口发送大量完全相同的请求,观察是否突破了业务限制。
推荐工具:Burp Suite 的 Turbo Intruder 插件。
- 优势:相比Burp自带的Intruder,Turbo Intruder专为高速并发攻击而设计,效率更高,能更好地模拟高并发场景。
- 使用步骤:
- 在Burp中拦截需要测试的请求(如点击“领取优惠券”的数据包)。
- 右键点击该请求,选择
Extensions->Turbo Intruder->Send to turbo intruder。 - 在Turbo Intruder界面中,通常可以使用默认脚本,它已经配置为并发发送请求。
- 设置并发线程数和请求数量,然后启动攻击。
- 观察服务器的响应。如果多个请求都返回“领取成功”或类似信息,则证明存在并发漏洞。
五、 并发漏洞的防御方案
防御的核心在于消除“查询”和“更新”之间的时间窗口,确保校验和写操作的原子性。
-
数据库层面(最推荐、最根本的解决方案)
- 唯一性约束:在用户-优惠券关系表上,为
(用户ID, 优惠券ID)字段组合创建唯一索引。这样,当第二个并发请求尝试插入相同的记录时,数据库会直接报错,插入失败。 - 悲观锁:在事务中,使用
SELECT ... FOR UPDATE语句锁定相关数据行,直到事务提交后才会释放锁。其他请求在此期间必须等待,从而强制串行化操作。 - 乐观锁:在数据表中增加一个版本号(
version)字段。更新时,校验当前版本号是否与查询时一致,一致则更新并递增版本号。如果版本号不一致,说明数据已被其他请求修改,则本次更新失败。
- 唯一性约束:在用户-优惠券关系表上,为
-
应用层面
- 同步锁:在代码关键部分(如领取逻辑)使用同步机制(如Java的
synchronized关键字或ReentrantLock)。但需要注意,在分布式集群环境中,本地锁无效,必须使用分布式锁(如基于Redis或ZooKeeper)。 - 原子操作:利用Redis等中间件的原子操作(如
INCR,DECR)或Lua脚本来处理计数和判断,避免在应用代码中写复杂的逻辑。
- 同步锁:在代码关键部分(如领取逻辑)使用同步机制(如Java的
-
业务层面
- 在返回最终结果前,再次进行状态校验,作为最后一层保障。
六、 总结
| 关键点 | 描述 |
|---|---|
| 本质 | 高并发下对共享资源的无序访问导致的逻辑错误。 |
| 核心原因 | “查询”与“更新”操作非原子性,中间存在可被利用的时间窗口。 |
| 高发场景 | 任何有限制性的业务操作(领取、购买、支付、签到)。 |
| 测试手段 | 使用Turbo Intruder等工具模拟高并发请求。 |
| 根本防御 | 在数据库层面通过唯一约束或锁机制保证原子性。 |
| 误区提醒 | 仅在前端或应用层做校验无法防御此漏洞,必须在服务端和数据库层面解决。 |
并发漏洞是Web应用中常见且危害较大的逻辑漏洞,开发者在设计相关功能时必须具备并发安全意识,测试人员在审计时应将并发测试作为一项常规项目。