Files
tamabox/outputs/p1-fixes-2026-08-31.md
T
tamakyi e3e59e7c69 chore: baseline snapshot before moderation-strictness feature
Repo .git was damaged (refs/ missing, index truncated, object store near-empty).
Repaired via git init (rebuilt refs), stale index preserved as .git/index.corrupt-20260910.bak,
then full worktree committed as a rollback baseline.
2026-09-10 10:56:27 +08:00

4.2 KiB
Raw Blame History

P1 修复落地记录(P1-3 / 4 / 5 / 7 / 8 / 9 / 10)

  • 日期:2026-08-31
  • 范围:按用户要求顺序实施 7 个 P1 项(P1-6 密码比较不在本次范围;P1-3 按要求不使用 Turnstile/recaptcha)。
  • P0-1(IP 伪造)暂缓,按用户指示留待后续。

1. P1-3 验证码限流 + 常量时间比较

文件:internal/captcha/math.go、internal/route/route.go

  • 答案比对改为 crypto/subtle.ConstantTimeCompare,消除时序侧信道。
  • VerifyOnly(模态框预校验)累加尝试次数,达到 maxCaptchaAttempts=5 即销毁该验证码,防止无限轮询爆破。
  • GenerateMath 处理 rand.Int 错误(不再忽略)。
  • /api/captcha/generate、/api/captcha/verify 加上 rateLimitMiddleware(20, 1m)。
  • ⚠️ 这是缓解而非根治:数学答案仍返回给客户端,脚本理论上可读题。根治依赖 P0-1(IP 信任校验)落地后整体提权。

2. P1-4 追问"等主人回复"服务端校验

文件:internal/db/follow_ups.go、route/question/follow_up.go

  • 新增 FollowUps.GetLatestByQuestionID(Order(created_at DESC) Limit 1)。
  • PublishFollowUp 在计数校验后,取该问题最新一条追问;若其 Answer == "" 直接拒绝(不再仅依赖前端 waitingFollowUpReply 隐藏表单)。

3. P1-5 审核可信域名精确匹配

文件:internal/security/censor/text.go

  • RemoveTrustedURL 改为边界感知:仅当可信域名后紧跟 /、?、# 或位于字符串结尾时才剥离,避免 https://github.com.evil.tld 被误判为 github.com 子域而漏审。

4. P1-7 删除回退功能配额计数

文件:internal/db/feature_control.go、internal/db/questions.go、internal/db/conversation.go

  • IncrementQuota 负 delta 增加 WHERE quota_count > 0 守卫,防止计数减到负数。
  • questions.DeleteByID 删除提问后回退 FeatureQuestion 配额 -1。
  • conversations.MarkDeletedByOwner 删除私聊会话后回退 FeatureChat 配额 -1(并先取 OwnerID,已删除/不存在时幂等返回)。

5. P1-8 自动封禁仅计真正 404

文件:internal/route/autoban.go、internal/route/route.go

  • autobanMiddleware 改为接收共享 *autoban.Autoban 实例(由 New() 创建一次)。
  • 真正的 404 探测计数移入 f.NotFound 处理器单独记录;不再把业务性 302 → /(语言切换 / 登出 / 投票不存在 / 黑名单跳转)误判为路径扫描导致误封。

6. P1-9 服务反代路径穿越 / query / 超时

文件:route/service/proxy.go

  • forwardPath 经 path.Clean 归一化,singleJoiningSlash 安全拼接,杜绝 ../ 越出白名单前缀。
  • 透传原始 RawQuery(此前静默丢弃)。
  • 复用带超时的共享 Transport(ResponseHeaderTimeout=15s、IdleConnTimeout=30s),不再每请求新建。

7. P1-10 像素追踪路由收紧

文件:internal/route/route.go、internal/context/context.go、route/pixel/pixel.go

  • /api/v1/pixel/{**} 由 f.Any 改为 f.Get + rateLimitMiddleware(30, 1m),仅放行只读 GET,并补限流。
  • context.go 去掉对 /api/v1/pixel/ 的 CSRF 豁免(GET 本不需 CSRF,写方法已被路由层禁止)。
  • pixel.go:路径 path.Clean 后必须仍在 /api/ 之下,否则拒绝;响应头仅透传白名单(Content-Type/Length/Encoding/Disposition/Cache-Control/ETag/Expires/Last-Modified/Pragma/Vary),不透传 Set-Cookie 等。

验证状态

  • ✅ go build / go vet 全绿(不依赖 storage 的 5 个包):internal/captcha、internal/security/censor、internal/db、route/pixel、route/service。
  • ✅ 单元测试通过:internal/security/censor、internal/security/autoban。
  • ✅ 12 个改动文件 gofmt -w 已格式化。
  • ⚠️ internal/route/route.go、internal/route/autoban.go、internal/context/context.go、route/question/follow_up.go 因传递依赖 internal/storage(github.com/chai2010/webp 缺 libwebp,Windows 沙箱 CGO 限制)无法在此环境完整编译;已用 gofmt -e 确认无语法错误,请在 Linux / 带 libwebp 环境做完整 go build ./... 验证。