Files
tamabox/outputs/autoban-false-positive-2026-09-05.md
T
2026-09-08 10:10:44 +08:00

6.2 KiB
Raw Blame History

autoban 自动封禁误伤诊断报告

日期:2026-09-05 · 分析对象:internal/security/autoban + internal/route/autoban.go + internal/shared/ip.go

一、当前配置与生效机制

配置 你的值 sample 默认 语义
trust_proxy true true 信任 CDN 权威头解析客户端 IP
enable_auto_ban true true 启用自动封禁
ban_threshold_sensitive 10 10 窗口内敏感路径命中 ≥10 触发封禁
ban_threshold_404 20 20 窗口内 404 ≥20 触发封禁
ban_window_seconds 10 60 统计窗口(滚动)
ban_duration_minutes 15 15 封禁时长

判定逻辑(autoban.go Record / IsBanned):

  • 每个请求按「归一化客户端 IP」计数(IPv4 原样;IPv6 聚合到 /64 → LimitKey)
  • 两类事件独立计数,敏感≥10 OR 404≥20(10s 滚动窗口内)任一达标 → 封禁该键 15 分钟
  • 已封禁键上所有后续请求直接 403

两个计数入口(全站仅此两处):

  1. 中间件 autoban.go:39:请求路径命中敏感词表(/admin、/.env、/wp-admin、/config、/db…共 39 条前缀)→ defer 计数
  2. f.NotFound(route.go:403):一切未匹配路由 → 404 计数后 Redirect("/")

二、排除项(已静态验证,非误伤来源)

  • ✅ 业务路由零碰撞:全站注册路由 + 模板 77 个站内 URL 与 39 条敏感前缀无一匹配——正常页面打开不会误计敏感
  • ✅ favicon.ico / robots.txt / sitemap / llms 均有 embed handler,不产生 404
  • ✅ 404 处理器与业务性 302(语言切换/登出/投票不存在)已分离,不误计

三、误伤根因(按可能性排序)

🔴 P0-A:客户端 IP「键折叠」→ 多用户共享同一计数键(最可能)

正常用户自己 10s 内绝不可能凑出 10 次敏感或 20 次 404(每秒 2 个 404 的强度),所以能误伤正常用户,唯一机制是"别人的探测被记到了你的键上"。trust_proxy=true 的解析链(ip.go ResolveClientIP):

  1. 先看 CF-Connecting-IP / EO-Client-IP(权威头)
  2. 都没有 → 回退 RemoteAddr

折叠场景:

  • 若 EO 回源未开启/未注入 EO-Client-IP(需在 EO 控制台确认回源透传配置),国内请求全部回退到 EO 边缘 IP(43.174.247.x 新加坡) → 全体大陆用户共享一个键。扫描器也走同一边缘 → 任一扫描触发 → 全国用户 403 15 分钟
  • 海外 CF tunnel:cloudflared 必注 CF-Connecting-IP,正常不折叠;但若异常缺失则回退 127.0.0.1 → 所有海外用户共享同一键
  • sample 注释原话即为该坑:"否则解析到的将是 Cloudflare 边缘 IP,自动封禁会误伤所有访客"

自检方法:配置 security_alert_email,看封禁告警里的 IP —— 若出现 43.174.247.43 / 43.174.246.43 / 127.0.0.1 这类非用户 IP,即实锤折叠。

🔴 P0-B:运营商 CGNAT / 共享出口连坐(国内常态)

国内移动/联通/电信家宽普遍 NAT444,成百上千真实用户共用一个公网 IPv4。该池内任一被黑设备/扫描器探测你的站点(攻击者扫全网,你的 EO 域名是公网目标)→ 触发阈值 → 整个出口池 403 15 分钟。你本人被误伤大概率由此而来(你的出口 IP 里别人在扫站)。

🟠 P1-C:IPv6 /64 聚合连坐

LimitKey 将 IPv6 聚合到 /64。若运营商分配粒度粗(多户共 /64)或会话前缀抖动,同段邻居的探测会连坐你。相对少见,但配置了 IPv6 解析时存在。

🟠 P1-D:双 CDN 权威头互信被伪造(定向误伤)

若未按 outputs/edgeone-cloudflare-dual-cdn-guide.md 在两家 CDN 规则引擎删除"对方 CDN"的权威头,攻击者可伪造 CF-Connecting-IP: <受害者IP> 直连源站/借道 EO → 探测全部记到受害者头上 → 把任意人"定向打成 403"。属攻击向量而非随机误伤,但同样表现为"我没干什么就 403"。

四、修复建议(分级)

P0-① 先止血(纯配置,立即可做,无需改代码)

ban_window_seconds = 60          ; 10s 窗口对正常浏览太苛刻,回到默认
ban_threshold_sensitive = 30     ; 敏感命中门槛抬高(真扫描 1s 内远超此值)
ban_threshold_404 = 100          ; 404 门槛抬高(正常用户+偶发爬虫不会到)
security_alert_email = <你的邮箱> ; 关键:先打开告警看封禁 IP,定位是否"键折叠"

窗口越短反而越难误伤,你已比默认更保守仍误伤 → 证明误伤不是参数敏感,而是键共享,调参只能缓解不能根治。

P0-② 确认并修复 IP 解析(治本,需查服务器/控制台)

  1. 源站侧:抓一条真实请求,确认带没带 EO-Client-IP / CF-Connecting-IP 头(nginx access log 或临时 echo 头调试)
    • EO 域名确认开启「回源客户端 IP 透传」;无则开
  2. 源站锁定:防火墙仅放行两家 CDN 回源网段(sample 已警告;未锁定时任何人可直连伪造头)
  3. 双 CDN 按指南删对端权威头

P1-① 代码增强(需你批准后实施)

项 改动 效果
登录用户豁免 有有效 session 的请求不计数/不封禁 管理员/正常登录用户永不误伤(扫描器无 cookie)
封禁日志 封禁时 logrus 打印 ip+路径样本+UA 事后可归因,不再"黑盒"
UA 特征二次确认 敏感命中要求 UA 缺浏览器特征才加速计数 降低误伤

P2 观察项

  • 若确认 P0-A 折叠实锤:可考虑对"CDN 边缘类"键整体排除(仅当 RemoteAddr 属于 CDN 且权威头缺失时才回退 RemoteAddr——需小改 ip.go)
  • 监控告警邮件中的封禁频率与 IP 类型分布

五、证据清单(本次分析依据)

  • internal/security/autoban/autoban.go:阈值判定、窗口、清理
  • internal/route/autoban.go:中间件挂载、403 直接返回
  • internal/route/route.go:78-84、146、403:实例化、全局挂载、404 计数
  • internal/shared/ip.go:ResolveClientIP 信任链 + LimitKey /64 聚合 + 双 CDN 警告注释
  • conf/app.sample.ini:默认值与设计约束说明
  • 静态碰撞分析:业务路由/模板 URL × 敏感词表 = 零命中