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.
94 lines
6.2 KiB
Markdown
94 lines
6.2 KiB
Markdown
# 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 × 敏感词表 = 零命中
|