bindbox-game/docs/赠送资产漏洞核查报告.md
win 8d1eef2f7f fix(channel): 修复渠道统计GMV重复计数和商城直购误计入
1. 排除商城直购(source_type=1):GMV和成本过滤条件从IN(1,2,3,4)改为IN(2,3,4)
2. 排除次卡免费使用订单(actual_amount=0):避免购买次卡和使用次卡双重计入GMV
   - source_type=4 一番赏使用次卡:1578单 44032元重复
   - source_type=3 对对碰使用次卡:422单 7042元重复
   - 合计去除51074元虚增GMV(29.1%)
3. 成本过滤条件同步修正:source_type IN(2,3,4),total_amount>0

修正后:GMV从175600降至124527元,毛利率从37.4%回到真实的11.8%
2026-03-16 21:41:39 +08:00

7.1 KiB
Raw Blame History

赠送资产漏洞核查报告

数据源: dev_game 数据库 | 核查日期: 2026-03-11


一、结论摘要

项目 结论
并发漏洞 确实存在已修复SELECT FOR UPDATE + RowsAffected 检查)
实际货物损失(一份发两份) 0 元 — 18 个重复发货资产中,没有一个真正被两方都发了货
积分重复兑换 0 元 — 没有任何资产被多人兑换积分,也没有同一资产被兑换多次
发送方转赠后又兑换同一资产 0 笔 — 发送方没有在转赠后兑换过同一资产
转赠后接收方兑换积分 91 笔 / 12,088.80 元 — 合法行为,资产转赠后归接收方所有

总实际损失: 0 元


二、漏洞技术分析

2.1 Bug 描述

文件: internal/service/user/address_share.go

Bug 位置 描述 后果
readDB 竞态 原 L116-133 反重复检查使用从库readDB主从延迟 10-100ms 内并发请求可绕过 同一资产产生重复转赠记录和重复发货记录
RowsAffected 未检查 原 L181-189 Updates() 返回 0 行影响时不报错,后续操作继续执行 资产状态未变但发货记录已创建

2.2 修复方案(已合并 zuncle 分支)

修复 方式
竞态条件 在事务内使用 SELECT FOR UPDATE 锁行 + 写库查询发货记录
RowsAffected 转赠和原主发货两个分支都检查 result.RowsAffected == 0 后回滚

2.3 关于"转赠资产禁止兑换积分"

zuncle 分支原本还包含第三个修复:禁止通过转赠获得的资产兑换积分。经核实,这是业务策略而非修漏洞

  • 资产转赠后归接收方所有,接收方有权决定发货或兑换积分
  • 发送方没有对已转赠的资产做任何兑换操作
  • 不存在"一份资产两边都兑换"的情况

已回退此限制。


三、数据核查

3.1 全局概况

指标 数值
总转赠记录数 157 条
涉及资产数 135 个
涉及用户数 15 人
多次转赠资产 13 个(并发 bug 导致的重复记录)
两方都产生发货记录的资产 18 个

3.2 重复发货资产明细18 个)

同一资产在发送方和接收方名下都产生了发货记录

仅接收方有效发货发送方全取消8 个)— 无损失

资产ID 价值(元) 发送方 接收方 接收方状态 发送方状态 兑换积分
49426 1,315.00 9248(不出last退了) 9116(非洲人) 有效1/取消1 全取消(2)
47096 900.00 9305(新人) 9116(非洲人) 有效1/取消1 全取消(3)
51038 605.00 9210(非酋) 9116(非洲人) 有效1/取消3 全取消(1)
44153 375.00 9248(不出last退了) 9116(非洲人) 有效1/取消2 全取消(1)
44152 375.00 9248(不出last退了) 9116(非洲人) 有效1/取消4 全取消(1)
44151 375.00 9248(不出last退了) 9116(非洲人) 有效1/取消2 全取消(1)
44150 375.00 9248(不出last退了) 9116(非洲人) 有效1/取消2 全取消(1)

小计: 4,320 元,全部仅接收方 9116 有效发货,无实际损失

双方全取消10 个)— 无损失

资产ID 价值(元) 发送方 接收方 接收方兑换积分
42746 375.00 9336(有冰的帝君) 9116(非洲人)
42757 375.00 9336(有冰的帝君) 9116(非洲人)
42758 375.00 9336(有冰的帝君) 9116(非洲人)
43304 375.00 9305(新人) 9116(非洲人)
42761 375.00 9116(非洲人) 9305(新人)
46445 375.00 9230(巨欧小肥龙) 9116(非洲人)
46446 375.00 9230(巨欧小肥龙) 9116(非洲人)
46447 375.00 9230(巨欧小肥龙) 9116(非洲人)
46506 375.00 9209(程c) 9116(非洲人)
46507 375.00 9209(程c) 9116(非洲人)
52338 12.50 9094(范巴斯滕) 9449(古利特)

小计: 双方发货全取消,接收方后续兑换了积分 — 这属于接收方对自有资产的合法操作

3.3 积分兑换核查

核查项 结果
同一资产被多个用户兑换积分 0 个
同一资产被同一用户多次兑换积分 0 个
发送方在 points_ledger 中兑换已转赠资产 0 笔
发送方的已转赠资产 remark 含 redeemed 1 笔(用户 9116 转出给 9305 的资产 43304后又转回 9116 兑换,属于正常来回转赠)

3.4 转赠后接收方兑换积分明细

以下为合法行为,资产转赠后归接收方所有,接收方有权兑换

用户ID 昵称 兑换笔数 兑换金额(元) 性质
9116 非洲人 30 10,737.00 合法 — 接收转赠后兑换
9110 极品官方内部号 24 446.60 合法
9305 新人 1 375.00 合法
9209 程c 9 220.00 合法
9222 嗯?!!!! 15 180.00 合法
9336 有冰的帝君 1 60.00 合法
9094 范巴斯滕救了一个美女 2 25.00 合法
9210 非酋 3 22.50 合法
9449 古利特使出了佛怒火莲 1 12.50 合法
9248 不出last退了 3 8.60 合法
9365 未命名 1 1.00 合法
9230 巨欧小肥龙 1 0.60 合法
合计 91 12,088.80 全部合法

3.5 多次转赠记录(并发 bug 产生的脏数据)

资产ID 转赠次数 路径 说明
42746 4 9336→9116 x4 同一操作并发触发4次
46668 4 9210→9116 x4 同上
43304 3 9305→9116, 9116→9305, 9305→9116 正常来回转赠
44152 3 9248→9116 x3 并发触发3次
46445 3 9230→9116 x3 并发触发3次
46667 3 9210→9116 x3 并发触发3次
51038 3 9210→9116, 9116→9210, 9210→9116 正常来回转赠
其他 6 个 2 并发触发2次

这些重复记录是脏数据,但未造成资产复制或积分重复。


四、修复状态

修复项 状态 Commit
SELECT FOR UPDATE 防并发转赠 已合并 main 8229b41
RowsAffected 检查防静默失败 已合并 main 8229b41
禁止转赠资产兑换积分 已回退 749464c,属业务策略非 bug 修复

五、结论

  1. 并发 bug 确实存在readDB 竞态 + RowsAffected 未检查,导致同一资产产生重复转赠记录和重复发货记录
  2. 实际经济损失为 0 元
    • 没有任何资产被真正发了两份货(重复发货记录中,发送方一侧全部已取消)
    • 没有任何资产被重复兑换积分
    • 发送方没有在转赠后又兑换同一资产的积分
  3. 转赠后兑换积分是合法行为:资产转赠后归接收方,接收方有权兑换
  4. Bug 已修复,防止未来可能的真正损失