vr-shopxo-plugin/docs/council-eval-securityengine...

8.1 KiB
Raw Blame History

Council 评估报告SecurityEngineer — Round 3 最终版

日期2026-05-26 审计范围支付链路购物车→支付→QR票生成+ Issue #6 + FOR UPDATE SKIP LOCKED + 前端XSS


一、现状评估

vr-shopxo-plugin 票务链路安全水位:中高。核心安全机制均已到位QR签名HMAC-SHA256+8字符截断、短码混淆Feistel网络、悲观锁核销verifyTicket事务+FOR UPDATE、幂等发票order_id+seat_info唯一键、ShopXO原子库存扣减。无发现 P0 安全漏洞。


二、安全问题全面审计

S-1issueTicket() 并发竞态 — P0 建议不等同P0漏洞

文件TicketService.php:151-154

$existing = Db::name(BaseService::table('tickets'))
    ->where('order_id', $order['id'])
    ->where('seat_info', $spec_name)
    ->find();   // ← TOCTOU 窗口

实际风险分析

  • ShopXO 的 onOrderPaid 回调由内核触发,在支付流水通知层面已有幂等保护
  • 只有在 ShopXO 重试回调 + 库存扣减成功但票未生成的极边缘场景下才会并发调用 issueTicket
  • 此时 ShopXO 层的原子条件 UPDATE WHERE inventory >= N 已经完成了库存扣减,超卖不会发生

缓解措施:已有 ShopXO 原子扣减作为主要防线issueTicket 幂等检查作为第二层。建议加唯一索引 uk_order_seat(order_id, seat_info) 作为根本性防护,但不应急迫。

结论⚠️ 可接受,建议修复但可延后


S-2FOR UPDATE SKIP LOCKED 概念澄清 — P2

实际情况

场景 实现 防护有效性
并发下单扣库存 ShopXO WHERE inventory >= N + dec() 原子UPDATE 有效
并发发票 issueTicket() 幂等检查(无行锁) ⚠️ 建议加唯一索引
并发核销 verifyTicket() lock(true) (FOR UPDATE) 有效

FOR UPDATE SKIP LOCKED 在此代码库中不是防超卖的关键。真正防超卖的是 ShopXO 的原子条件 UPDATE不是行锁。verifyTicket 的 FOR UPDATE 已有SKIP LOCKED 是优化而非必须。

结论 可接受,概念澄清完毕


S-3QR Secret 硬编码 — P1

文件BaseService.php:298-302

private static function getVrSecret(): string
{
    // $secret = env('VR_TICKET_SECRET', '');  // 注释掉了
    $secret = '8935b3a3-a7b4-4e3d-8c1f-9b7e2a6f5d4c';  // ← 硬编码 fallback
    if (empty($secret)) {
        throw new \Exception('请在.env中设置VR_TICKET_SECRET=...');
    }
    return $secret;
}

注意getQrSecret()用于QR加密强制要求配置 .env,未配置则抛异常。而 getVrSecret()用于HMAC签名有硬编码 fallback。

风险:源码泄露时,攻击者可伪造短码。但 HMAC 签名本身(使用 goods_id 派生 key仍提供了一定隔离。

缓解:生产环境确保 .env 中配置 VR_TICKET_SECRET

结论⚠️ P1建议确认.env配置可延后至安全专项


S-4$goodsId 未定义导致 ClearCache 失效 — P3 Bug

文件TicketService.php:126

SeatMapService::ClearCache(intval($goodsId));  // $goodsId 在 onOrderPaid 中未定义

影响:座位图缓存在支付后不会被清除,下一位买家可能看到过期已售座位。不影响票务安全链路(库存以 ShopXO 数据库为准)。

正确代码应为

SeatMapService::ClearCache(intval($og['goods_id']));

结论🟢 P3 Bug不影响安全可延后修复


S-5前端 XSS$goods['content'] 未转义)— P3

文件ticket_detail.html:75

<?php echo $goods['content']; ?>  // 管理后台富文本直接输出

管理员可注入恶意JS但风险范围仅限管理后台高信任用户。观演人表单字段real_name、phone、id_card均已使用 htmlspecialchars() 转义,状态良好。

结论🟢 P3管理面可控可延后


三、安全防线评估矩阵(最终版)

威胁 保护机制 强度 状态
超卖(并发下单) ShopXO dec() 原子条件UPDATE 🟢 已防护
超卖(同一座位被多次发票) issueTicket() 幂等检查 🟡 建议加唯一索引
伪造QR票 HMAC-SHA256签名ticket_code绑定 🟢 已防护
伪造短码 Feistel混淆+goods_id派生key 🟡 已防护
重复核销 verifyTicket() FOR UPDATE 悲观锁 🟢 已防护
QR票过期重放 30分钟 exp + iat + code 字段 🟢 已防护
环境密钥泄露 getVrSecret() 硬编码 fallback 🔴 ⚠️ 需确认.env
前端XSS 富文本XSS管理面可控 🟡 ⚠️ 可延后

四、Issue #6 结论

当前代码中无 P0 安全漏洞。 所有问题均为 P1-P3不应作为主攻方向阻塞项。


五、安全维度投票

议题:下一步主攻方向

投票C — 双线并行

理由

  1. 安全所有问题均为 P1-P3无 P0 漏洞,不应阻塞开发主轴
  2. 后端 API 完善seatSpecMap Hook 注册、extension_data 链路)是当前唯一真正的阻塞点
  3. 安全加固(.env确认、P1唯一索引可以与前端开发并行推进不互相依赖
  4. 最大化团队效率,同时保证安全改进不遗漏

对其他提案的评估

  • A后端优先:合理,但完全阻塞前端会浪费 H5 保底能力和前端团队资源
  • B前端优先H5 票务页是好的过渡,但不能忽视 uniapp 是主要目标
  • DPhase 4 优先)Tree API 是差异化功能Phase 3 核心流程尚未完全稳定,不应跳级


六、Round 4 现场核查确认

安全行动项现场核实

S-4 Bug 确认TicketService.php:126

SeatMapService::ClearCache(intval($goodsId)); // $goodsId 未定义

确认存在。正确代码应为 $og['goods_id']。此 Bug 不影响安全ShopXO DB 库存为准),但会导致座位图缓存失效迟缓,影响用户体验。

S-3 QR Secret 硬编码确认BaseService.php:302

$secret = '8935b3a3-a7b4-4e3d-8c1f-9b7e2a6f5d4c'; // 确认存在

确认存在。生产环境需配置 .envVR_TICKET_SECRET

S-1 幂等检查确认TicketService.php:151-154

$existing = Db::name(BaseService::table('tickets'))
    ->where('order_id', $order['id'])
    ->where('seat_info', $spec_name)
    ->find();   // 无行锁,但有幂等保护

确认存在。ShopXO onOrderPaid 由内核回调触发,已在支付流水层有幂等保护。唯一建议是加唯一索引 uk_order_seat(order_id, seat_info),但可延后。

BackendArchitect P0 重分类背书

BackendArchitect Round 4 修正了 Round 1-3 的 P0 评估:

  • getSoldSeats() 方法缺失 → 已消除SeatMapService::GetSeatMap() 已含库存)
  • Index.php::soldSeats Fatal Error → 已消除Index.php 无此 action
  • plugins_service_goods_data Hook 未注册 → 降为 P1UniApp 可用 /seatmap 变通)

背书 BackendArchitect 的 P0 重分类。与我的安全评估一致:无 P0 安全漏洞。


七、安全维度投票Round 4 确认)

议题:下一步主攻方向 投票C双线并行Round 1/2/3/4 一致,不变)

理由

  1. 安全所有问题均为 P1-P3无 P0 安全漏洞,不应阻塞开发主轴
  2. 后端 P0 重分类(无 P0确认安全不阻塞后端 API 完善Hook 注册、seatSpecMap 注入)是当前真正阻塞点
  3. 安全加固(.env 确认、P1 唯一索引)可与前端开发并行推进,不互相依赖
  4. H5 ticket_detail.html 可作为独立过渡方案,立即推进 loadSoldSeats() 实现

补充BackendArchitect 投票 A后端优先有合理性Hook 注册是最小改动最大收益),但前端 H5 无阻塞可立即推进,两者不冲突。


报告人SecurityEngineer | 2026-05-26 | Round 4 核查完成