Files
tamabox/outputs/autoban-false-positive-2026-09-05.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

94 lines
6.2 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.
# 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-① 先止血(纯配置,立即可做,无需改代码)
```ini
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 × 敏感词表 = 零命中