feat: brute-force protection with per-IP escalating lockout

- Failures counted per client IP (X-Forwarded-For last hop behind the
  proxy); after maxFailures within the window the IP is locked out,
  doubling per repeat up to lockoutMaxSeconds.
- Locked IPs get 429 + Retry-After (login page / JSON for API), and the
  scrypt verification is skipped entirely while locked (no CPU burn).
- Fixed failedLoginDelayMs delay on every bad credential attempt.
- Basic-auth path counts and clears identically; success resets the IP.
- All thresholds configurable; in-memory state only.
- Tests: lockout, expiry restore, basic-path counting, XFF last-hop key.
This commit is contained in:
dsh
2026-08-16 01:48:36 -04:00
parent 000ca41501
commit 8823049b66
3 changed files with 176 additions and 10 deletions
+28
View File
@@ -35,6 +35,34 @@
> IP 说明:面板经 Caddy 反代时,直连 socket 是回环地址;插件在检测到回环
> 来源时会取 `X-Forwarded-For` 的首个值作为真实 IP(Caddy 默认会带上该头)。
## 防爆破(默认开启)
- **按 IP 计数**:同一 IP 在 `failureWindowSeconds`(默认 300 秒)窗口内连续
`maxFailures`(默认 5)次密码错误后进入锁定期。
- **阶梯式锁定**:首次锁 `lockoutBaseSeconds`(默认 30 秒),再次触发翻倍,
上限 `lockoutMaxSeconds`(默认 3600 秒)。
- 锁定期内:登录页返回 `429 + Retry-After`"尝试次数过多"),API 返回
`429 {"error":"too many attempts"}`,且**不再执行 scrypt 校验**(不消耗 CPU)。
- **失败延迟**:每次密码错误额外等待 `failedLoginDelayMs`(默认 300ms),
拖慢单连接暴力尝试。
- **成功即清零**;锁定状态为进程内存态,面板重启后清空(重启面板需要
root,攻击者无法自行重置)。
- Basic 认证路径同样计入;IP 取自反代 `X-Forwarded-For` 的最后一跳
(Caddy 会覆写伪造值,见下)。
- 配置(`cordis.patch.yml``config` 中可调):
```yaml
bruteProtection: true # 关闭设为 false
maxFailures: 5
lockoutBaseSeconds: 30
lockoutMaxSeconds: 3600
failureWindowSeconds: 300
failedLoginDelayMs: 300
```
> 局限:分布式攻击(每尝试换一个 IP)不受单 IP 锁定约束;这由
> 300ms 失败延迟 + scrypt 慢哈希兜底。公网反代场景建议再配合 Caddy
> 层的 IP 白名单/云防火墙(如 Cloudflare)使用。
## 修改密码
```bash