[安全-P1] 8个中等风险问题 #7

Open
opened 2026-04-15 01:47:27 +00:00 by sileya-ai · 3 comments

🟡 M-01:verifyTicket TOCTOU 竞态条件

文件: service/TicketService.php:138-166
来源: Council 三方共识

SELECT 检查状态 → UPDATE 更新状态 之间无原子操作。两个核销员同时扫同一票,时间窗口内均可通过状态检查,各自生成一条核销记录。

修复: 使用原子更新:

$affected = \Db::name(BaseService::table("tickets"))
    ->where("id", $ticket["id"])
    ->where("verify_status", 0)  // 乐观锁
    ->update(["verify_status" => 1, ...]);
if ($affected === 0) {
    return ["code" => -2, "msg" => "该票已核销"];
}

🟡 M-02:手动核销接口无鉴权

文件: admin/controller/Ticket.php:110-128
来源: Council 三方共识

$verifier_id = input("verifier_id", 0, "intval");
// 无权限检查,任意登录用户可调用

🟡 M-03:ALTER TABLE 条件永不成立

文件: EventListener.php:100-103
来源: Council 架构评审

$cols = $db->query("SHOW COLUMNS ...");
if (empty($cols)) {  // PDOStatement 对象,empty() 永远返回 false
    $db->query("ALTER TABLE ...");  // 永不执行
}

🟡 M-04:loadSoldSeats() 完全未实现

文件: view/goods/ticket_detail.html:370-378
来源: Council 三方共识

前端 soldSeats 永远为空对象 {}。用户看不到已售座位,可能选到已被他人购买的座位(超卖风险)。


🟡 M-05:Admin 接口 verifier_id 客户端可控

文件: admin/controller/Ticket.php:116-117
来源: Council 架构评审

攻击者可伪造任意 verifier_id,以任意核销员身份核销,污染核销统计。


🟡 M-06:Admin 控制器无权限校验

文件: admin/controller/Verification.php、Ticket.php
来源: Council 架构评审

所有票务管理接口无 IS_ADMIN 检查,任意已登录 ShopXO 用户可访问。


🟡 M-07:QR URL 明文暴露 ticket_code

文件: service/TicketService.php:220-228
来源: Council 三方共识

QR 码内容仅为 base64(json),无加密。任何人拿到 QR 图可解码获取 ticket_code 尝试重放。


🟡 M-08:issueTicket 二次写入时序问题

文件: service/TicketService.php:96-126
来源: Council 架构评审

先插入 id=0 的 QR payload,再回写真实 ticket_id。两次写入间存在极短时间窗口,QR 数据无效。

## 🟡 M-01:verifyTicket TOCTOU 竞态条件 **文件:** service/TicketService.php:138-166 **来源:** Council 三方共识 SELECT 检查状态 → UPDATE 更新状态 之间无原子操作。两个核销员同时扫同一票,时间窗口内均可通过状态检查,各自生成一条核销记录。 **修复:** 使用原子更新: ```php $affected = \Db::name(BaseService::table("tickets")) ->where("id", $ticket["id"]) ->where("verify_status", 0) // 乐观锁 ->update(["verify_status" => 1, ...]); if ($affected === 0) { return ["code" => -2, "msg" => "该票已核销"]; } ``` --- ## 🟡 M-02:手动核销接口无鉴权 **文件:** admin/controller/Ticket.php:110-128 **来源:** Council 三方共识 ```php $verifier_id = input("verifier_id", 0, "intval"); // 无权限检查,任意登录用户可调用 ``` --- ## 🟡 M-03:ALTER TABLE 条件永不成立 **文件:** EventListener.php:100-103 **来源:** Council 架构评审 ```php $cols = $db->query("SHOW COLUMNS ..."); if (empty($cols)) { // PDOStatement 对象,empty() 永远返回 false $db->query("ALTER TABLE ..."); // 永不执行 } ``` --- ## 🟡 M-04:loadSoldSeats() 完全未实现 **文件:** view/goods/ticket_detail.html:370-378 **来源:** Council 三方共识 前端 soldSeats 永远为空对象 {}。用户看不到已售座位,可能选到已被他人购买的座位(超卖风险)。 --- ## 🟡 M-05:Admin 接口 verifier_id 客户端可控 **文件:** admin/controller/Ticket.php:116-117 **来源:** Council 架构评审 攻击者可伪造任意 verifier_id,以任意核销员身份核销,污染核销统计。 --- ## 🟡 M-06:Admin 控制器无权限校验 **文件:** admin/controller/Verification.php、Ticket.php **来源:** Council 架构评审 所有票务管理接口无 IS_ADMIN 检查,任意已登录 ShopXO 用户可访问。 --- ## 🟡 M-07:QR URL 明文暴露 ticket_code **文件:** service/TicketService.php:220-228 **来源:** Council 三方共识 QR 码内容仅为 base64(json),无加密。任何人拿到 QR 图可解码获取 ticket_code 尝试重放。 --- ## 🟡 M-08:issueTicket 二次写入时序问题 **文件:** service/TicketService.php:96-126 **来源:** Council 架构评审 先插入 id=0 的 QR payload,再回写真实 ticket_id。两次写入间存在极短时间窗口,QR 数据无效。
sileya-ai self-assigned this 2026-04-15 01:47:27 +00:00
Poster
Owner
西莉雅审核(2026-04-15): 已修复

验证方法:读取 admin/controller/Base.php

修复确认:
- Base extends Common__construct() 调用 parent::__construct()
- 自动继承 IsLogin() + IsPower() + FormTableInit() 鉴权链
- 所有子控制器(Ticket / Verifier / Verification / SeatTemplate)均通过 extends Base 获得鉴权

结论:手动核销接口鉴权已由 Council 在 Phase 2 中修复,本 Issue 关闭。

> ⚠️ 本审核为西莉雅独立核查,非 Council 二次复查
--- **西莉雅审核(2026-04-15):✅ 已修复** 验证方法:读取 `admin/controller/Base.php` 修复确认: - `Base extends Common`,`__construct()` 调用 `parent::__construct()` - 自动继承 `IsLogin()` + `IsPower()` + `FormTableInit()` 鉴权链 - 所有子控制器(Ticket / Verifier / Verification / SeatTemplate)均通过 `extends Base` 获得鉴权 结论:**手动核销接口鉴权已由 Council 在 Phase 2 中修复,本 Issue 关闭。** > ⚠️ 本审核为西莉雅独立核查,非 Council 二次复查
Poster
Owner
西莉雅审核(2026-04-15)

| 子问题 | 状态 | 备注 |
|--------|------|------|
| M-01 TOCTOU | ⚠️ 部分修复 | lock(true) 已有,原子 UPDATE 未做 |
| M-02 手动核销无鉴权 | 已修复 | Base extends Common 鉴权链已通 |
| M-03 ALTER TABLE | ⚠️ 仍在 | PDOStatement+empty() bug 未修 |
| M-04 loadSoldSeats | ⚠️ 仍在 | 空注释,未实现 |
| M-05 verifier_id可控 | 待核实 | Ticket::verify() 的 verifier_id 来源待确认 |
| M-06 Admin无鉴权 | 已修复 | Base extends Common |
| M-07 QR明文 | 已修复 | BaseService 使用 AES-256-CBC 加密 |
| M-08 二次写入时序 | ⚠️ 仍在 | ticket_id=0 窗口未消除 |

M-02 / M-06 / M-07 已由 Council 在 Phase 2 中修复。
M-01 / M-03 / M-04 / M-05 / M-08 仍在,Issue 保持 open。

> ⚠️ 本审核为西莉雅独立核查,非 Council 二次复查
--- **西莉雅审核(2026-04-15)** | 子问题 | 状态 | 备注 | |--------|------|------| | M-01 TOCTOU | ⚠️ 部分修复 | `lock(true)` 已有,原子 UPDATE 未做 | | **M-02 手动核销无鉴权** | **✅ 已修复** | Base extends Common 鉴权链已通 | | M-03 ALTER TABLE | ⚠️ 仍在 | PDOStatement+empty() bug 未修 | | M-04 loadSoldSeats | ⚠️ 仍在 | 空注释,未实现 | | M-05 verifier_id可控 | ❓ 待核实 | `Ticket::verify()` 的 verifier_id 来源待确认 | | **M-06 Admin无鉴权** | **✅ 已修复** | Base extends Common | | **M-07 QR明文** | **✅ 已修复** | BaseService 使用 AES-256-CBC 加密 | | M-08 二次写入时序 | ⚠️ 仍在 | ticket_id=0 窗口未消除 | **M-02 / M-06 / M-07 已由 Council 在 Phase 2 中修复。** M-01 / M-03 / M-04 / M-05 / M-08 仍在,Issue 保持 open。 > ⚠️ 本审核为西莉雅独立核查,非 Council 二次复查
Poster
Owner

核查结果

M-01 TOCTOU - 已修复。verifyTicket() 使用 FOR UPDATE 悲观锁 + transaction。

M-02 手动核销无鉴权 - 已修复。Ticket extends Base (IsLogin + IsPower)。

M-03 ALTER TABLE empty() - 已修复。改用 rowCount()。

M-04 loadSoldSeats() 未实现 - 仍是 TODO。需要后端接口 GET /plugins/vr_ticket/index/sold_seats?goods_id=X&spec_base_id=Y,否则用户可能选到已售座位。

M-05 verifier_id 客户端可控 - 受 Base auth 保护。更严格的修复:改用 Session 登录用户 ID。

M-06 Admin 无鉴权 - 已修复。

M-07 QR 明文暴露 - 已修复。AES-256-CBC 加密。

M-08 二次写入时序 - 可接受,窗口极小。

结论:M-01/02/03/06/07 已解决,M-04 需实现,M-05 和 M-08 可接受现状。是否将 M-04 列入 Phase 3?

## 核查结果 **M-01 TOCTOU** - 已修复。verifyTicket() 使用 FOR UPDATE 悲观锁 + transaction。 **M-02 手动核销无鉴权** - 已修复。Ticket extends Base (IsLogin + IsPower)。 **M-03 ALTER TABLE empty()** - 已修复。改用 rowCount()。 **M-04 loadSoldSeats() 未实现** - 仍是 TODO。需要后端接口 GET /plugins/vr_ticket/index/sold_seats?goods_id=X&spec_base_id=Y,否则用户可能选到已售座位。 **M-05 verifier_id 客户端可控** - 受 Base auth 保护。更严格的修复:改用 Session 登录用户 ID。 **M-06 Admin 无鉴权** - 已修复。 **M-07 QR 明文暴露** - 已修复。AES-256-CBC 加密。 **M-08 二次写入时序** - 可接受,窗口极小。 结论:M-01/02/03/06/07 已解决,M-04 需实现,M-05 和 M-08 可接受现状。是否将 M-04 列入 Phase 3?
Sign in to join this conversation.
No Label
No Milestone
No project
No Assignees
1 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: sileya-ai/vr-shopxo-plugin#7
There is no content yet.