From 2982819548f177a16a4883ee75b7fbce94afcfe5 Mon Sep 17 00:00:00 2001 From: yml2213 Date: Fri, 2 Oct 2026 17:09:33 +0800 Subject: [PATCH] =?UTF-8?q?fix(worker-risk):=20=E4=BC=AA=E9=80=A0=20uid=20?= =?UTF-8?q?=E6=97=B6=E9=A3=8E=E6=8E=A7=E4=BA=8B=E4=BB=B6=E5=9B=A0=E5=A4=96?= =?UTF-8?q?=E9=94=AE=E7=BA=A6=E6=9D=9F=E5=86=99=E4=B8=8D=E8=BF=9B=E5=8E=BB?= =?UTF-8?q?=EF=BC=8C=E6=94=B9=E4=B8=BA=E6=9F=A5=E4=B8=8D=E5=88=B0=E5=B0=B1?= =?UTF-8?q?=E5=AD=98=20NULL?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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)。 --- .../repositories/worker-platform/worker-risk-repo.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/apps/backend/src/repositories/worker-platform/worker-risk-repo.ts b/apps/backend/src/repositories/worker-platform/worker-risk-repo.ts index 83d6e566..97fb8572 100644 --- a/apps/backend/src/repositories/worker-platform/worker-risk-repo.ts +++ b/apps/backend/src/repositories/worker-platform/worker-risk-repo.ts @@ -50,7 +50,13 @@ export type InsertWorkerRiskEventInput = { detail?: Record } -/** 写入一条风控事件。账号名用子查询就地补全,避免调用方多发一次查询。 */ +/** + * 写入一条风控事件。账号名用子查询就地补全,避免调用方多发一次查询。 + * + * worker_id 同样用子查询解析,**查不到就写 NULL**:外挂可以伪造 token 里的 uid, + * 那种 uid 在 worker_users 里并不存在。若直接写该值会触发外键约束失败, + * 导致「最该留痕的伪造流量反而记不下来」。target_key 仍保留原始 uid,追溯不受影响。 + */ export async function insertWorkerRiskEvent(input: InsertWorkerRiskEventInput): Promise { 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() )