This commit is contained in:
ray zhou
2026-05-29 19:26:16 +08:00
parent ad623aad91
commit 225fb2bd28
7 changed files with 330 additions and 47 deletions

View File

@@ -0,0 +1,169 @@
---
name: callback_log 验签顺序
overview: 修订需求文档与 slot-pwa 实现:将「完整 callback_log 落库」从「收包第一时间」调整为「验签通过后」;验签失败仅保留应用日志,避免未授权请求刷写分库分表。
todos:
- id: update-docs-02
content: 修订 02_provider_callback_log.md流程图、§5 衔接、§6 验收、安全说明
status: completed
- id: update-docs-parent
content: 同步 game_gateway_risk_prd_v_2.md §4.3/§4.4 流程顺序
status: completed
- id: reorder-middleware
content: slot-pwaPOPAuth 在前ProviderCallbackLog 在后POPAuth 去掉 DB 验签回写
status: in_progress
- id: insert-verify-passed
content: insertReceived 验签通过态写入;清理 markVerifyPassed/Failed 在 POP 路径的调用
status: pending
- id: tests-verify
content: 更新/补充 callback_log 相关测试并跑 verify-slot-backend.sh
status: pending
isProject: false
---
# callback_log 改为验签通过后落库
## 问题判断
你的顾虑成立。当前设计与实现是:
```mermaid
sequenceDiagram
participant P as Provider
participant L as ProviderCallbackLogMiddleware
participant A as POPAuthMiddleware
participant C as Controller
P->>L: HTTP callback
L->>L: INSERT callback_log verify_status=0
L->>A: next
A->>A: verify signature
alt verify fail
A->>L: UPDATE verify_failed
A-->>P: 401 JSON
else verify pass
A->>C: next
end
```
- 需求:[02_provider_callback_log.md](docs/requirements/game_gateway/02_provider_callback_log.md) §3.1 / §5 要求「先 INSERT再验签」
- 实现:[middleware.php](slot-pwa/config/middleware.php) 中 `ProviderCallbackLogMiddleware` 排在 `POPAuthMiddleware` **之前**[ProviderCallbackLogMiddleware.php](slot-pwa/app/middleware/ProviderCallbackLogMiddleware.php) 注释也写明「须在验签之前」
- 验收 §6.2 要求「验签失败仍有 callback_log」——与防刷目标冲突
**原设计意图(合理但需边界)**:把每一次到达网关的请求都当作「证据」,包括验签失败,便于和 Provider 对账「对方声称发过回调」。代价是:公开 URL + 无 IP 白名单/限流时,攻击者可对 `user_transaction_*.game_provider_callback_log_`* 灌入大量 `MEDIUMTEXT``raw_body`/`raw_headers`)。
**你已确认的产品取舍**:验签通过后才 INSERT **完整** callback_log验签失败不落库仅应用日志后续可选轻量 reject 表,本期不做)。
这与资金安全主链路一致:父文档 [game_gateway_risk_prd_v_2.md](docs/requirements/game_gateway_risk_prd_v_2.md) §5.1 将「验签 / IP 校验」的目的写明为「防止非法回调」——应先挡住非法流量,再落审计库。
---
## 目标流程
```mermaid
sequenceDiagram
participant P as Provider
participant A as POPAuthMiddleware
participant L as ProviderCallbackLogMiddleware
participant C as Controller
P->>A: HTTP callback
A->>A: verify signature
alt verify fail
A->>A: structured error log only
A-->>P: auth error JSON
else verify pass
A->>L: next
L->>L: INSERT callback_log verify_status=1
L->>C: next
end
```
要点:
- **验签失败**`Log::error` 保留 `route``IP``body_hash`(可选)、`error`**不**调用 `markVerifyFailed`(因无 `log_id`
- **验签通过**`insertReceived` 时直接 `verify_status = VERIFY_PASSED(1)`,可删除事后 `markVerifyPassed` 的 UPDATE减少一次写
- **解析失败**:仍在验签通过且已落库后,由 Logic 更新 `parse_status` / `process_status`(现有 `markParseFailed` 等不变)
---
## 文档修订(必须)
### 1. [02_provider_callback_log.md](docs/requirements/game_gateway/02_provider_callback_log.md)
| 章节 | 修改 |
| ------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| §1.2 In scope | 「第一时间落库」改为「**验签通过后**同步落库完整原始请求」 |
| §3.1 流程图 | `Provider callback → 验签 → 同步写 callback_log(verify_status=1) → 解析 → …` |
| §5 衔接表 | 「收到任意回调先 INSERT」改为「验签通过后 INSERT」删除「验签完成再 UPDATE verify_status」作为主路径保留解析/处理状态更新) |
| §6 验收 | **改写 §6.2**:验签失败不落 callback_log但应用日志可按 `request_id`/时间检索;**新增**:任意成功进入业务链路的 `provider_tx` 仍可 `callback_log_id` 追溯 `raw_body` |
| 新增 §(安全说明) | 明确:防刷靠「验签门禁 +(建议)网关 IP 白名单/限流」;若未来需要攻击审计,用独立 reject 表或日志平台,不污染主 callback_log |
### 2. [game_gateway_risk_prd_v_2.md](docs/requirements/game_gateway_risk_prd_v_2.md)
- §4.3 / §4.4 流程图中「同步写 callback_log」节点移到「验签」**之后**(与 02 子文档一致)
---
## 代码修订slot-pwa
### 1. 调整 Middleware 顺序
[config/middleware.php](slot-pwa/config/middleware.php) `pop` 组改为:
```php
'pop' => [
\app\middleware\POPAuthMiddleware::class,
\app\middleware\ProviderCallbackLogMiddleware::class,
],
```
### 2. [POPAuthMiddleware.php](slot-pwa/app/middleware/POPAuthMiddleware.php)
- 验签失败:仅 `Log::error` + 返回 JSON**移除** `markVerifyFailed`(依赖已落库的 `log_id`
- 验签成功:**移除** `markVerifyPassed`;直接 `$handler($request)` 进入下一 Middleware 落库
- 平台配置缺失:同样不落库,只打日志返回错误
### 3. [ProviderCallbackLogMiddleware.php](slot-pwa/app/middleware/ProviderCallbackLogMiddleware.php)
- 更新类注释:在验签 Middleware **之后**执行,仅对验签通过的请求落库
- `recordReceivedFromRequest` 失败时:当前 catch 后仍 `return $handler`——建议改为 **记录错误并返回 5xx**(避免无 callback_log 却继续扣款);与产品确认可放在实现阶段一并收紧
### 4. [ProviderCallbackLogService.php](slot-pwa/app/service/game/ProviderCallbackLogService.php) + [GameProviderCallbackLogModel.php](slot-pwa/app/model/shard/GameProviderCallbackLogModel.php)
- `insertReceived` 增加参数或拆方法:`insertReceivedAfterVerifyPassed()`,写入时 `verify_status = VERIFY_PASSED`
- `markVerifyPassed` / `markVerifyFailed`保留方法但标注「仅兼容旧路径」或删除未引用调用POP 路径不再使用 failed 回写)
- `recordReceivedFromRequest` 仅在验签后 Middleware 调用,语义与命名可在 PHPDoc 中写清
### 5. 测试
- [GameProviderCallbackLogShardModelTest.php](slot-pwa/tests/Feature/GameProviderCallbackLogShardModelTest.php) / [ProviderCallbackLogTestCase.php](slot-pwa/tests/Support/ProviderCallbackLogTestCase.php):若直接测 `insertReceived`,默认 `VERIFY_PENDING` 仍可用于 Model 单测;新增或调整用例覆盖「验签后插入为 PASSED」
- 可选Middleware 顺序集成测试(验签失败不触发 insert——若项目已有 HTTP 测试基建则补一条
---
## 不在本期但必须知晓的风险
| 风险 | 缓解 |
| ------------------------------- | --------------------------------------------------------------------------------- |
| 验签失败无 DB 记录,与 Provider 扯皮时只能靠日志 | 日志字段标准化:`provider_code``source_ip``body_hash``x-request-id`;日志保留期与 Provider 对齐 |
| CPU 层仍可被刷验签 | 网关层 Provider IP 白名单 + rate limitnginx/云 WAF本期文档 § 安全说明中写为 **建议项** |
| `uid=0` 分片仍可能被合法但解析失败的请求写入 | 仅发生在验签通过后,可接受;与刷库攻击不同类 |
---
## 实施顺序建议
1. 先改 02 子文档 + 父文档流程图(避免实现与需求再漂移)
2. 调整 middleware 顺序与 POP / CallbackLog 职责
3. Model/Service 插入时带 `VERIFY_PASSED`
4. `按php-clean-code.mdc 文件验证代码规范`
5.`verify-slot-backend.sh` + 相关 PHPUnit