fix(security): 修复赠送资产薅积分三大漏洞
1. SELECT FOR UPDATE 锁定资产行,防止并发转赠竞态条件 2. 检查 RowsAffected 防止 GORM 静默失败导致空壳发货记录 3. 兑换积分时校验转赠来源,禁止转赠资产兑换积分 4. 转赠来源校验改用写库查询,避免主从延迟绕过 5. 转赠来源查询错误不再静默忽略,失败时返回错误 基于 zuncle 分支修复,额外修正了两个安全隐患: - RedeemInventoryToPoints/RedeemInventoriesToPoints 中 转赠记录查询从 readDB 改为 writeDB - Count()/Find() 返回的 error 不再丢弃
This commit is contained in:
parent
bd91c0fad1
commit
9cf9f798bb
@ -570,10 +570,11 @@ func (s *service) RedeemInventoryToPoints(ctx context.Context, userID int64, inv
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 校验转赠来源:通过转赠获得的资产不允许兑换积分(防薅积分漏洞)
|
// 校验转赠来源:通过转赠获得的资产不允许兑换积分(防薅积分漏洞)
|
||||||
transferCnt, _ := s.readDB.UserInventoryTransfers.WithContext(ctx).Where(
|
// 使用写库查询,避免主从延迟导致校验被绕过
|
||||||
s.readDB.UserInventoryTransfers.InventoryID.Eq(inventoryID),
|
var transferCnt int64
|
||||||
s.readDB.UserInventoryTransfers.ToUserID.Eq(userID),
|
if err := s.repo.GetDbW().Raw("SELECT COUNT(*) FROM user_inventory_transfers WHERE inventory_id = ? AND to_user_id = ?", inventoryID, userID).Scan(&transferCnt).Error; err != nil {
|
||||||
).Count()
|
return 0, err
|
||||||
|
}
|
||||||
if transferCnt > 0 {
|
if transferCnt > 0 {
|
||||||
return 0, fmt.Errorf("transfer_inventory_cannot_redeem")
|
return 0, fmt.Errorf("transfer_inventory_cannot_redeem")
|
||||||
}
|
}
|
||||||
@ -659,14 +660,15 @@ func (s *service) RedeemInventoriesToPoints(ctx context.Context, userID int64, i
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 3.5 排除通过转赠获得的资产(防薅积分漏洞)
|
// 3.5 排除通过转赠获得的资产(防薅积分漏洞)
|
||||||
|
// 使用写库查询,避免主从延迟导致校验被绕过
|
||||||
invIDs := make([]int64, 0, len(invList))
|
invIDs := make([]int64, 0, len(invList))
|
||||||
for _, inv := range invList {
|
for _, inv := range invList {
|
||||||
invIDs = append(invIDs, inv.ID)
|
invIDs = append(invIDs, inv.ID)
|
||||||
}
|
}
|
||||||
transferredInvs, _ := s.readDB.UserInventoryTransfers.WithContext(ctx).
|
var transferredInvs []*model.UserInventoryTransfers
|
||||||
Where(s.readDB.UserInventoryTransfers.InventoryID.In(invIDs...)).
|
if err := s.repo.GetDbW().Raw("SELECT * FROM user_inventory_transfers WHERE inventory_id IN ? AND to_user_id = ?", invIDs, userID).Scan(&transferredInvs).Error; err != nil {
|
||||||
Where(s.readDB.UserInventoryTransfers.ToUserID.Eq(userID)).
|
return 0, err
|
||||||
Find()
|
}
|
||||||
transferredSet := make(map[int64]struct{}, len(transferredInvs))
|
transferredSet := make(map[int64]struct{}, len(transferredInvs))
|
||||||
for _, t := range transferredInvs {
|
for _, t := range transferredInvs {
|
||||||
transferredSet[t.InventoryID] = struct{}{}
|
transferredSet[t.InventoryID] = struct{}{}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user