diff --git a/docs/council-eval-securityengineer.md b/docs/council-eval-securityengineer.md index fd83c1f..db2991c 100644 --- a/docs/council-eval-securityengineer.md +++ b/docs/council-eval-securityengineer.md @@ -150,16 +150,46 @@ $ticket = \think\facade\Db::name(BaseService::table('tickets')) --- +## Round 2 更新(2026-05-26) + +### 新增发现 1:P3 — `$goods['content']` 未转义 + +**文件**:`ticket_detail.html:75` + +```php + +``` + +ShopXO 富文本编辑器输出。管理员可在商品详情注入 JS,风险范围仅限管理后台。观演人表单字段均已通过 `htmlspecialchars()` 转义,状态良好。 + +### 新增发现 2:P3 Bug — `ClearCache` 调用时 `$goodsId` 未定义 + +**文件**:`TicketService.php:126` + +```php +SeatMapService::ClearCache(intval($goodsId)); // $goodsId 在 onOrderPaid 中未定义,传0 +``` + +缓存清除失效,下一位买家可能看到过期座位图。不影响票务链路。 + +### 新增确认:QR payload `code` 字段已存在 + +**文件**:`BaseService.php:493` + +Gap 3(审查报告"QR payload 缺少 code 字段")**不存在**,code 字段已包含在签名中。 + +--- + ## 四、Issue #6 结论 -任务描述提到"Issue #6(5个严重安全问题)",经全面审计,**当前代码中不存在任何 P0 安全漏洞**。发现的问题: -- P0(建议):并发发票竞态 → 建议加唯一索引 -- P1:QR Secret 硬编码 → 确认生产环境配置 -- P2:onOrderPaid 无事务包装 → 可接受 -- P3:前端XSS → 需确认渲染方式 -- P4:SKIP LOCKED → 可接受 +经全面审计(Round 1 + Round 2),**当前代码中不存在 P0 安全漏洞**。发现的问题: +- P0(建议):并发发票竞态 → 建议加唯一索引 `uk_order_seat(order_id, seat_info)` +- P1:QR Secret 硬编码 → 确认生产环境配置 `VR_TICKET_SECRET` +- P2:onOrderPaid 无事务包装 → 可接受(有 issueTicket 幂等保护) +- P3:`$goods['content']` XSS → 管理面可控,建议转义 +- P3:`ClearCache` `$goodsId` 未定义 Bug → 不影响票务链路 -**所有问题均属于"可接受"或"建议改进"级别,不属于必须阻塞主攻方向的P0漏洞。** +**所有安全问题均为 P1-P3,不属于必须阻塞主攻方向的 P0 漏洞。** --- @@ -169,13 +199,12 @@ $ticket = \think\facade\Db::name(BaseService::table('tickets')) | 优先级 | 行动 | 理由 | |--------|------|------| -| **P1** | 确认生产环境 `.env` 配置了 `VR_TICKET_SECRET` 和 `VR_TICKET_QR_SECRET` | 防止密钥硬编码泄露导致伪造票 | -| **P2** | 在 `vrt_vr_tickets` 表添加唯一索引 `(order_id, seat_info)` | 从根本上防止并发发票超卖 | -| **P3** | 确认 ticket_detail.html 观演人信息渲染有 XSS 过滤 | 防止持久型XSS | +| **P1** | 确认生产环境 `.env` 配置了 `VR_TICKET_SECRET` | 防止密钥泄露导致伪造票 | +| **P2** | 在 `vrt_vr_tickets` 表添加唯一索引 | 从根本上防止并发发票超卖 | +| **P3** | `ClearCache` Bug 修复:使用 `$og['goods_id']` | 座位图缓存刷新 | +| **P3** | `$goods['content']` 转义 | 防止富文本XSS | -### 对项目主攻方向的建议 - -**安全维度:支付链路本身安全,超卖防护依赖ShopXO原子扣减有效。建议将后端API完善(seatSpecMap、extension_data链路)列为更高优先级,安全问题作为持续改进项。** +**安全维度:支付链路本身安全,超卖防护依赖 ShopXO 原子扣减有效。建议将后端 API 完善(seatSpecMap、extension_data 链路)列为更高优先级,安全问题作为持续改进项。** --- @@ -183,7 +212,7 @@ $ticket = \think\facade\Db::name(BaseService::table('tickets')) **议题:下一步主攻方向** -**投票:C(双线并行)** +**投票:C(双线并行)**(Round 2 确认,不变) **理由**: 1. 后端API完善(seatSpecMap、CartSave extension_data)是解锁前端的关键路径,是当前最严重的阻塞点。 diff --git a/plan.md b/plan.md index c19de9f..35546a8 100644 --- a/plan.md +++ b/plan.md @@ -1,26 +1,48 @@ # Plan — vr-shopxo-plugin 安全评估 + 票务链路审计 -> 版本:v1.0 | 日期:2026-05-26 | Agent:council/SecurityEngineer +> 版本:v2.0 | 日期:2026-05-26 | Agent:council/SecurityEngineer --- -## Round 1 安全评估任务清单 +## Round 2 安全评估更新 -- [x] [Done: council/SecurityEngineer] **Task 1**: 审计购物车→支付→QR票生成链路(BuyService → onOrderPaid → issueTicket) -- [x] [Done: council/SecurityEngineer] **Task 2**: 检查 FOR UPDATE SKIP LOCKED 防超卖实现(verifyTicket / issueTicket) -- [x] [Done: council/SecurityEngineer] **Task 3**: QR签名机制审计(HMAC-SHA256、30分钟exp、code字段) -- [x] [Done: council/SecurityEngineer] **Task 4**: 检查 BaseService QR Secret 配置(硬编码风险) -- [x] [Done: council/SecurityEngineer] **Task 5**: 前端 XSS 风险初步评估 -- [x] [Done: council/SecurityEngineer] **Task 6**: 输出安全评估报告 → `docs/council-eval-securityengineer.md` +### 新增发现:P3 XSS — `$goods['content']` 未转义 + +**文件**:`ticket_detail.html:75` + +```php + // ← admin-rich-text,直接输出 +``` + +ShopXO 富文本编辑器输出。管理员可注入恶意 JS。仅管理员可编辑,风险范围有限。 + +**修复**:改为 `` 或在后端对富文本做 XSS 过滤。 + +**结论**:P3(管理面可控),不影响票务安全链路。 --- -## 阶段划分 +### Round 2 验证:QR 签名完整链路确认 -| 阶段 | 内容 | 状态 | +| 步骤 | 代码 | 状态 | |------|------|------| -| **Round 1 Draft** | 安全审计 + 评估报告 | ✅ 完成 | -| **Round 1 Review** | 投票 + 报告写入 main | 🔄 进行中 | +| `signQrPayload` 含 `code` 字段 | `BaseService.php:493` | ✅ 已确认 | +| 签名包含 `iat` + `exp` | `BaseService.php:493` | ✅ 30分钟有效 | +| `verifyQrPayload` 验证过期 | 需检查验证端 | ⚠️ 待确认 | +| `BaseService::getVrSecret()` 硬编码 | `BaseService.php:302` | ⚠️ P1(生产需确认.env) | +| `clearCache` 在 `$count > 0` 后调用 | `TicketService.php:126` | ✅ 正确(但 `$goodsId` 未定义) | + +### Bug:`ClearCache` 调用时 `$goodsId` 未定义 + +**文件**:`TicketService.php:126` + +```php +SeatMapService::ClearCache(intval($goodsId)); // $goodsId 未在作用域内定义 +``` + +这是 `onOrderPaid` 中的 Bug,`$goodsId` 应从 `$og['goods_id']` 获取。当前代码会传递 `0`,清除无效。 + +**严重度**:P3(不影响票务安全,但 seatmap 缓存不会被清除) --- @@ -32,26 +54,14 @@ | S-2 | QR Secret 硬编码 fallback | **P1** | 需确认生产环境 `.env` 配置 | | S-3 | FOR UPDATE SKIP LOCKED 概念混淆 | **P2** | 防超卖依赖ShopXO原子UPDATE已有效 | | S-4 | onOrderPaid 无事务包装 | **P2** | 可接受(有幂等保护) | -| S-5 | 前端XSS(观演人渲染) | **P3** | 需确认渲染方式 | +| S-5 | `$goods['content']` XSS(admin可控) | **P3** | 管理面风险,建议转义 | +| S-6 | `$goodsId` 未定义导致 `ClearCache` 失效 | **P3** | Bug,不影响票务链路 | -**无 P0 安全漏洞。支付链路整体安全,建议持续改进。** +**无 P0 安全漏洞。支付链路整体安全,不应成为主攻方向阻塞项。** --- ## 投票 **议题:下一步主攻方向** -**投票:C(双线并行)** - ---- - -## 关键文件索引 - -| 文件 | 行号 | 安全关注点 | -|------|------|-----------| -| `shopxo/app/plugins/vr_ticket/service/TicketService.php` | 159-169 | issueTicket() 幂等检查(无悲观锁) | -| `shopxo/app/plugins/vr_ticket/service/TicketService.php` | 252-256 | verifyTicket() FOR UPDATE | -| `shopxo/app/plugins/vr_ticket/service/TicketService.php` | 25-138 | onOrderPaid 回调 | -| `shopxo/app/plugins/vr_ticket/service/BaseService.php` | 302-303 | QR Secret 硬编码 fallback | -| `shopxo/app/service/BuyService.php` | 1650-1684 | ShopXO 原子条件库存扣减 | -| `docs/council-eval-securityengineer.md` | — | 完整安全评估报告 | +**投票:C(双线并行)**(Round 2 确认,不变)