Files
tamabox/.workbuddy/memory/topics/review.md
T

62 lines
5.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 审核 / 处置 / 举报(长版明细)
## 审核 / 处置 / 举报
- 线上通道 = 七牛(`ai.qiniuapi.com/v3/text/censor`,antispam);`[security] text_censor_source = qiniu|aliyun|auto`。
七牛只硬拦涉政/暴恐/色情,`review` 放行 → 裁决在 `censor/policy.go` + `moderation.Check`。
- **新增审核调用点必须走 `moderation.Check`**(直调 `censor.Text` 绕过档位)。不可用时 **fail-closed**。
- ⚠️ 圈内黑话(代称+群体标签+避雷叙述)通用审核拦不住,别指望词库;靠处置 + 举报 + 人工判断。
- 严格度 lax/standard/strict 存 `users.moderation_strictness`;**受处置用户强制 strict**:
`moderation.EffectiveStrictness(ctx, userID, raw)`(7 个入口已改)。**查询失败回退用户设置**
(否则 DB 抖动会让全站突然加严)。处置期内拒绝保存 + 设置页 disabled。
- ⚠️ **同请求渲染只能用 `ctx.SetError` / `SetInternalError`,不能用 `*Flash`**:flash 写 session,下次请求才读 →
提示静默丢失。`Set*Flash + Redirect` 才是正常模式。扫描法:找 flash 调用 → 向下第一个 `Success(`/`Redirect(`。
- 处置四级:L1 停止分发/L2 收窄公开(新回答强制私密、禁 set-public)/L3 冻结发布(仍可收信)/
L4 整页停用(`/restricted`)。到期靠 `expires_at`,**无 cron**;`restrict` 是**替换语义**;比较用 `RestrictionAtLeast`。
- ⚠️ L2 强制私密必须在**审核通过之后**,且与写回答**同一条 UPDATE**(`AnswerByIDPrivate`):
拆开会留下「公开+已作答」窗口,第二条失败还会变成「私密+无回答」。
- ⚠️ **`ListAPI`(`/api/v1/user/{domain}/questions`)不经过 Pager 中间件**,L4 与页面密码必须自己校验。
- ⚠️ **整箱分享卡 `ShareCard` 与单题分享卡都要拦**,且拦截必须在缓存查询之前(否则 30 分钟内仍给旧图)。
- 举报 `POST /_/{domain}/{qid}/report`:免登录 + 验证码 + 限流;同 IP 同题唯一索引 →
**幂等且提示与首次一致**(否则成了内容存在性探测器)。箱主只看「被举报 N 次」,**不给 IP**。
- 闭环:删除/设私密/`hide-public` → `MarkAcceptedByQuestion`;待处理超 30 天视为过期。
⚠️ `hide-public` 里必须**先快照目标 ID 再更新**(更新后带 `is_private=false` 的子查询恒为空集)。
- 告警:24h≥8 或 7d≥20 → 邮件到 `[security] security_alert_email`(24h 冷却);单条恰好 3 人举报 → 提醒箱主。
⚠️ 该邮箱为空时静默跳过、无日志;改配置必须重启。
- `/rules` 是处置依据页(四语言);注册页必须勾 `agree_rules`(服务端同样校验),改 e2e 脚本要同步。
- `restrict`/`unrestrict` 默认给箱主发通知(按 `users.language`,`mail/restriction-notice.html`),
**只含级别名/到期/规则链接,不含 `--reason`**;`--no-notify` 跳过。`--days > 90` 要 `--force`。
⚠️ **到期自动解除不发信**(读表比较时间)。
## 蜜罐(2026-10-21)
- 落点是**改写 `f.NotFound`**(不是新中间件):NotFound 只在路由表落空时触发 → 与业务路由零交集;
位于全局 Use 链之后(body 已限 12 MiB);组级 CSRF 不作用于它。
- flamego 短路机制:`context.run()` 在 `ResponseWriter().Written()` 为真时 return → 写完响应不调 `ctx.Next()` 即止。
- 蜜罐命中的路径由蜜罐自己 `autoban.Record`,autoban 中间件须跳过(否则一次请求计两遍,阈值减半)。
- `HoneypotHit` 不嵌 `dbutil.Model`(审计证据只追加,不软删)。
- 假页只含 canary + 假信息;错误提示不区分「用户不存在」与「密码错误」(防用户名枚举)。
- 密码 CLI 默认脱敏;告警邮件含明文凭据 → 收件人只能是运维本人。
### 蜜罐封禁与 enable_auto_ban 解耦(2026-10-21 线上实测修复)
- ⚠️ **`Record()` 第一行 `if !b.enabled { return }` 会让 `enable_auto_ban=false` 连坐蜜罐** →
蜜罐只落库不封禁(实测命中 70+ 次零封禁)。已修:新增 `RecordHoneypot()` 独立通道 + 独立阈值/时长。
- `IsBanned()` 不得受 `enabled` 约束:`enabled` 只控制「是否主动创建封禁」,不控制「是否尊重已有封禁」。
- `RecordHoneypot` 的键必须与 `Record` 一致(同样 `shared.LimitKey` 归一化),否则 `IsBanned` 判不出来。
- 备份/配置/密钥泄露扫描(`/backup.zip`、`/db.sql`、`/id_rsa.pem`)文件名主体随机,**只有后缀稳定**
→ 用 `path.Base()` 后缀匹配表,不能只靠子串(`"/.zip"` 匹配不到 `/backup.zip`)。
### 蜜罐表体积与自动清理(2026-10-21)
- `honeypot_hits` 是**只追加**审计表,写入完全由外部扫描流量驱动(不可预期、不可节流)
→ 必须有自动保留期清理,否则表体积只增不减。
- 两道闸:时间(`honeypot_retain_days`)+ 数量(`honeypot_max_rows`)。数量闸不可省 ——
攻击者可用海量源 IP 在保留期内把表撑爆。
- ⚠️ PG 的 `DELETE` 只标记死元组,**表文件不会变小**;无 VACUUM 时表体积单调膨胀
(实测 22→52 MB)。清理后必须 `VACUUM (ANALYZE)`,但注意 **VACUUM 不缩小文件、
只让空间可复用**(止住增长即可,不必 `VACUUM FULL`)。
- ⚠️ `VACUUM` 不能在事务块/函数体内执行 → GORM 走 `Exec()`(Raw 处理器,不包事务)可行。
- ⚠️ 清理器的 `runOnce` 必须自己再挡一次 `retainDays<=0`,否则「保留 0 天」= 全删光。
- 索引:GORM 去掉 struct tag 的 `index` **不会**删既有库上的旧索引 → 需显式 DropIndex。
删索引前先用 EXPLAIN 确认零查询使用(`provider` 索引实测 10 万行 648 kB 且零使用)。
- CLI `honeypot size` 报告行数/磁盘占用/时间跨度 —— 把「有没有臃肿」交给运维自查。