Files
tamabox/outputs/shared-egress-vote-antispam-review-2026-08-26.md
T
2026-09-08 10:10:44 +08:00

3.4 KiB
Raw Blame History

共享出口匿名投票防刷修改与测试报告

  • 时间:2026-08-26
  • 目标:解决 Cloudflare + trust_proxy=true 场景下,局域网用户共享公网 IP 导致同一投票只能一人提交的问题。
  • 策略:签名 vote_fp 负责设备级一人一票;IP 仅用于高阈值突发风控,不再承担同一投票的一票封顶。

修改内容

1. 删除匿名投票的 IP 一票封顶

route/vote/vote.go:

  • 移除提交处理器内 CountByIP(... 24h) 且 count >= 1 即拒绝的逻辑。
  • 匿名用户继续通过有效 HMAC vote_fp 与数据库唯一索引防止同一浏览器/设备重复投票。
  • 无效或缺失 Cookie 仍会被重新签发,不会成为可信身份。

2. 新增投票提交专用的身份优先限流

internal/route/ratelimit.go:

新增 voteSubmitRateLimitMiddleware,投票提交同时经过两层限制:

  • 登录用户:按 user:<uid> 独立限流,额度为常规参数的 3 倍。
  • 匿名用户:按有效 token:<vote_fp> 独立限流。
  • 所有匿名请求:同时经过 ip:<client-ip> 高阈值兜底限流,默认是传入基础额度的 10 倍。
  • 无效/缺失 Cookie 不会获得独立 token 限流身份,只能使用 IP 兜底。
  • 触发限制时返回 HTTP 429,不进入实际投票提交处理器。

internal/route/route.go 已将 /vote/{token}/submit 从普通 IP 限流切换到该专用中间件。

3. 校准 Cloudflare 客户端 IP 解析

internal/shared/ip.go:

  • TrustProxy=true 时优先读取 Cloudflare 官方 CF-Connecting-IP。
  • 若该头不存在或非法,再回退到 X-Real-IP、X-Forwarded-For,最后使用 RemoteAddr。
  • TrustProxy=false 时仍完全忽略这些请求头,只使用 RemoteAddr。
  • 新增 Cloudflare 头优先级和不信任代理时忽略头部的测试。

4. 更新设计文档

anonymous-vote-design.md 已同步新的防刷矩阵、token 流程和共享出口说明。

验证结果

通过:

gofmt -d internal/route/ratelimit.go internal/route/route.go \
  internal/shared/ip.go route/vote/vote.go
# 无输出

go test -count=1 ./internal/shared ./route/vote ./internal/template \
  ./templates ./internal/i18n ./internal/cache ./internal/security/autoban
# 全部 PASS

go vet ./route/vote ./internal/shared ./internal/template ./templates ./internal/i18n
# 无输出

补充测试覆盖:

  • 同一共享 IP 下多个不同 token 彼此独立;
  • 同一 token 仍受独立限流;
  • IP 兜底允许多次正常请求,但能阻断异常突发;
  • Cloudflare CF-Connecting-IP 优先于其他代理头;
  • 未启用可信代理时伪造的代理头不会生效。

go test ./internal/route 当前仍无法编译完成,原因是项目既有的 github.com/chai2010/[email protected] 在本机缺少 CGO/WebP 编译符号,并非本次限流逻辑测试失败。

Cloudflare 部署注意事项

  1. 源站防火墙应只允许 Cloudflare 官方 IP 段访问源站端口,否则攻击者可能直接访问源站并伪造 CF-Connecting-IP。
  2. 保持 trust_proxy=true 的前提是源站确实位于可信反向代理之后。
  3. 重要投票建议开启 Turnstile 或要求登录;匿名签名 Cookie 只能保证设备级防重,无法阻止清 Cookie、换浏览器或自动化客户端。
  4. 当前共享 IP 兜底阈值为 200 次/分钟(投票路由传入基础值 20),可根据真实并发量调整。