fix(worker-risk): 伪造 uid 时风控事件因外键约束写不进去,改为查不到就存 NULL

worker_risk_events.worker_id 有指向 worker_users(id) 的外键。
外挂可以伪造 token 里的 uid(中间件只 base64 解 payload、不验签就以该 uid 建跟踪维度),
那种 uid 在 worker_users 里并不存在,直接写入会触发外键约束失败 ——
结果是**最该留痕的伪造流量反而记不下来**,而且因为留痕是 fire-and-forget,
失败只在日志里留一条,界面上完全看不出来。

生产实测(真实库事务内回滚验证):
  worker_id=41(真实账号)       -> 插入成功
  worker_id=99999999(伪造 uid) -> ERROR: violates foreign key constraint

改为用子查询解析 worker_id,查不到即写 NULL;target_key 仍保留原始 uid,
追溯能力不受影响。修复后实测两种情形均写入成功,伪造者 worker_id 为 NULL。

顺带确认:留痕链路本身是通的(后台 release 接口已实际写入过一条 manual_unbanned)。
This commit is contained in:
yml2213
2026-10-02 17:09:33 +08:00
parent 78c676d2b9
commit 2982819548
@@ -50,7 +50,13 @@ export type InsertWorkerRiskEventInput = {
detail?: Record<string, unknown>
}
/** 写入一条风控事件。账号名用子查询就地补全,避免调用方多发一次查询。 */
/**
* 写入一条风控事件。账号名用子查询就地补全,避免调用方多发一次查询。
*
* worker_id 同样用子查询解析,**查不到就写 NULL**:外挂可以伪造 token 里的 uid,
* 那种 uid 在 worker_users 里并不存在。若直接写该值会触发外键约束失败,
* 导致「最该留痕的伪造流量反而记不下来」。target_key 仍保留原始 uid,追溯不受影响。
*/
export async function insertWorkerRiskEvent(input: InsertWorkerRiskEventInput): Promise<void> {
await query(
`
@@ -59,7 +65,8 @@ export async function insertWorkerRiskEvent(input: InsertWorkerRiskEventInput):
violation_count, window_count, penalty_until,
action, actor_user_id, actor_username, reason, detail_json, created_at
) VALUES (
$1, $2, $3,
$1, $2,
(SELECT id FROM worker_users WHERE id = $3),
COALESCE((SELECT username FROM worker_users WHERE id = $3), ''),
$4, $5, $6, $7, $8, $9, $10, $11, $12::jsonb, NOW()
)