详解并发漏洞
字数 2257 2025-10-18 11:17:50

并发逻辑漏洞详解教学文档

一、 核心概念:什么是并发漏洞?

并发漏洞,也称为竞态条件漏洞,是一种在高并发场景下,由于程序对共享资源(如余额、库存、领取状态)的访问顺序处理不当,导致实际执行结果与开发者预期不符的安全问题。

核心原因:多线程或多进程环境中,开发者在处理共享数据时,缺乏必要的同步控制机制(如加锁),或者同步逻辑存在缺陷,使得多个请求能够几乎同时通过关键校验点。

二、 并发漏洞的常见业务场景

该漏洞通常出现在存在“数量限制”或“状态校验”的业务逻辑中,例如:

  1. 签到/抽奖:限制用户每日只能签到或抽奖一次。
  2. 优惠券/积分领取:限制每个用户只能领取一张优惠券或固定数额的积分。
  3. 购买限制:限制用户购买特价商品的数量。
  4. 支付漏洞:多个并发支付请求可能只进行一次实际的余额扣减,导致用户以极低成本完成多次交易。
  5. 商品库存/余额更新:并发下单可能导致超卖(库存减为负数)或余额扣减不正确。

三、 漏洞产生的根本原理:并发 vs. 串行

要理解漏洞,必须区分两种程序执行模式:

  • 串行操作:请求依次处理。必须等待上一个请求完全执行完毕(包括数据写入数据库)后,才会开始处理下一个请求。这是开发者通常预期的逻辑。
  • 并发操作:多个请求交替或同时执行。现代Web服务器为提升性能,会采用多线程技术同时处理多个请求。

漏洞产生过程(以“并发领取优惠券”为例):
一个正常的领取流程涉及三张表:用户表、优惠券表、用户-优惠券关系表。理想中的串行流程如下:

  1. 用户A请求领取。
  2. 服务器查询关系表,检查用户A是否已领取。
  3. 如果未领取,则向关系表中插入一条领取记录。
  4. 返回领取成功。

然而,在并发环境下,如果没有同步控制,流程可能如下:

  1. 请求A 到达服务器,执行查询,发现用户未领取。
  2. 请求A 向数据库插入记录之前请求B 也到达服务器,并执行查询。此时由于请求A的插入操作尚未完成,请求B的查询结果同样是“用户未领取”。
  3. 请求A请求B 都基于“未领取”的判断,先后向数据库插入了领取记录。
  4. 结果:用户成功领取了两张优惠券,绕过了限制。

根本原因:在“查询”和“更新”(写库)两个操作之间存在一个时间窗口。并发请求在这个时间窗口内同时通过了校验。

四、 并发漏洞的测试方法

测试并发漏洞的核心是:在极短时间内,向目标接口发送大量完全相同的请求,观察是否突破了业务限制。

推荐工具:Burp Suite 的 Turbo Intruder 插件。

  • 优势:相比Burp自带的Intruder,Turbo Intruder专为高速并发攻击而设计,效率更高,能更好地模拟高并发场景。
  • 使用步骤
    1. 在Burp中拦截需要测试的请求(如点击“领取优惠券”的数据包)。
    2. 右键点击该请求,选择 Extensions -> Turbo Intruder -> Send to turbo intruder
    3. 在Turbo Intruder界面中,通常可以使用默认脚本,它已经配置为并发发送请求。
    4. 设置并发线程数和请求数量,然后启动攻击。
    5. 观察服务器的响应。如果多个请求都返回“领取成功”或类似信息,则证明存在并发漏洞。

五、 并发漏洞的防御方案

防御的核心在于消除“查询”和“更新”之间的时间窗口,确保校验和写操作的原子性。

  1. 数据库层面(最推荐、最根本的解决方案)

    • 唯一性约束:在用户-优惠券关系表上,为(用户ID, 优惠券ID)字段组合创建唯一索引。这样,当第二个并发请求尝试插入相同的记录时,数据库会直接报错,插入失败。
    • 悲观锁:在事务中,使用SELECT ... FOR UPDATE语句锁定相关数据行,直到事务提交后才会释放锁。其他请求在此期间必须等待,从而强制串行化操作。
    • 乐观锁:在数据表中增加一个版本号(version)字段。更新时,校验当前版本号是否与查询时一致,一致则更新并递增版本号。如果版本号不一致,说明数据已被其他请求修改,则本次更新失败。
  2. 应用层面

    • 同步锁:在代码关键部分(如领取逻辑)使用同步机制(如Java的synchronized关键字或ReentrantLock)。但需要注意,在分布式集群环境中,本地锁无效,必须使用分布式锁(如基于Redis或ZooKeeper)。
    • 原子操作:利用Redis等中间件的原子操作(如INCR, DECR)或Lua脚本来处理计数和判断,避免在应用代码中写复杂的逻辑。
  3. 业务层面

    • 在返回最终结果前,再次进行状态校验,作为最后一层保障。

六、 总结

关键点 描述
本质 高并发下对共享资源的无序访问导致的逻辑错误。
核心原因 “查询”与“更新”操作非原子性,中间存在可被利用的时间窗口。
高发场景 任何有限制性的业务操作(领取、购买、支付、签到)。
测试手段 使用Turbo Intruder等工具模拟高并发请求。
根本防御 数据库层面通过唯一约束锁机制保证原子性。
误区提醒 仅在前端或应用层做校验无法防御此漏洞,必须在服务端和数据库层面解决。

并发漏洞是Web应用中常见且危害较大的逻辑漏洞,开发者在设计相关功能时必须具备并发安全意识,测试人员在审计时应将并发测试作为一项常规项目。


并发逻辑漏洞详解教学文档 一、 核心概念:什么是并发漏洞? 并发漏洞,也称为竞态条件漏洞,是一种在 高并发场景 下,由于程序对 共享资源 (如余额、库存、领取状态)的访问顺序处理不当,导致实际执行结果与开发者预期不符的安全问题。 核心原因 :多线程或多进程环境中,开发者在处理共享数据时, 缺乏必要的同步控制机制 (如加锁),或者同步逻辑存在缺陷,使得多个请求能够几乎同时通过关键校验点。 二、 并发漏洞的常见业务场景 该漏洞通常出现在存在“数量限制”或“状态校验”的业务逻辑中,例如: 签到/抽奖 :限制用户每日只能签到或抽奖一次。 优惠券/积分领取 :限制每个用户只能领取一张优惠券或固定数额的积分。 购买限制 :限制用户购买特价商品的数量。 支付漏洞 :多个并发支付请求可能只进行一次实际的余额扣减,导致用户以极低成本完成多次交易。 商品库存/余额更新 :并发下单可能导致超卖(库存减为负数)或余额扣减不正确。 三、 漏洞产生的根本原理:并发 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脚本来处理计数和判断,避免在应用代码中写复杂的逻辑。 业务层面 在返回最终结果前,再次进行状态校验,作为最后一层保障。 六、 总结 | 关键点 | 描述 | | :--- | :--- | | 本质 | 高并发下对共享资源的无序访问导致的逻辑错误。 | | 核心原因 | “查询”与“更新”操作非原子性,中间存在可被利用的时间窗口。 | | 高发场景 | 任何有限制性的业务操作(领取、购买、支付、签到)。 | | 测试手段 | 使用 Turbo Intruder 等工具模拟高并发请求。 | | 根本防御 | 在 数据库层面 通过 唯一约束 或 锁机制 保证原子性。 | | 误区提醒 | 仅在前端或应用层做校验无法防御此漏洞,必须在服务端和数据库层面解决。 | 并发漏洞是Web应用中常见且危害较大的逻辑漏洞,开发者在设计相关功能时必须具备并发安全意识,测试人员在审计时应将并发测试作为一项常规项目。