fix(waf): harden against OOM and Slowloris DoS attacks
- P0: add 10MB hard request body size limit with io.LimitReader, return 413 Request Entity Too Large when exceeded, prevents OOM - P0: configure http.Server timeouts: ReadHeaderTimeout=10s (Slowloris), ReadTimeout=15s, WriteTimeout=30s, IdleTimeout=120s, MaxHeaderBytes=1MB - P1: properly handle body read errors, log and return on client disconnect - P2: drain and close r.Body in defer to ensure TCP connection reuse - Log errors from ReadRequestBodyFrom and ProcessRequestBody - Inject security headers on 413 responses - Add security audit report (docs/SECURITY_AUDIT_20260801.md) - Update README with timeout/body-limit docs and changelog
This commit is contained in:
1 parent
bd544a4181
commit
b4436b192a
4 files changed
+430
-15
No files matched your search
+46
-7
@@ -248,14 +248,36 @@ kill -HUP $(pgrep waf)
|
||||
|
||||
## 请求处理流程
|
||||
|
||||
[proxy.go:81-138](internal/proxy/proxy.go#L81) `ServeHTTP`:
|
||||
[proxy.go:81-172](internal/proxy/proxy.go#L81) `ServeHTTP`:
|
||||
|
||||
1. `ProcessConnection` + `ProcessURI` + 添加请求头(含 Host)
|
||||
2. `ProcessRequestHeaders` → 命中即 `block` 返回 403
|
||||
3. 读全量请求体 → `ReadRequestBodyFrom` → `ProcessRequestBody` → 命中即 block
|
||||
3. 读取请求体(**10MB 硬限制**,超限返回 413)→ `ReadRequestBodyFrom` → `ProcessRequestBody` → 命中即 block
|
||||
4. `httputil.ReverseProxy.ServeHTTP` 转发到 gateway
|
||||
5. 捕获响应头 → `ProcessResponseHeaders`
|
||||
5. 捕获响应头 → `ProcessResponseHeaders`(仅审计日志,不拦截)
|
||||
6. `ModifyResponse` 统一注入安全响应头(覆盖下游同名头)
|
||||
7. defer 中确保 `r.Body` 耗尽并关闭,保证 TCP 连接复用
|
||||
|
||||
### 请求体大小限制
|
||||
|
||||
WAF 在代码层面对请求体实施 **10MB 硬限制**([proxy.go#L19-L22](internal/proxy/proxy.go#L19-L22) `maxRequestBodySize`),与 `waf.conf` 中的 `SecRequestBodyLimit 10485760` 保持一致:
|
||||
|
||||
- 使用 `io.LimitReader` + 多读 1 字节检测超限
|
||||
- 超限直接返回 `413 Request Entity Too Large`,不经过 Coraza 规则引擎
|
||||
- 读取错误(客户端断开)记录日志后直接返回,不继续处理
|
||||
- 从根本上防止超大请求体导致 OOM 崩溃
|
||||
|
||||
### HTTP 服务器超时防护
|
||||
|
||||
[main.go#L111-L122](main.go#L111) 为 `http.Server` 配置了多层超时,防御 Slowloris 等慢连接 DoS 攻击:
|
||||
|
||||
| 参数 | 值 | 作用 |
|
||||
|------|-----|------|
|
||||
| `ReadHeaderTimeout` | 10s | 读取请求头超时,**直接防御 Slowloris** |
|
||||
| `ReadTimeout` | 15s | 读取整个请求(含 body)超时,防慢 POST |
|
||||
| `WriteTimeout` | 30s | 写入响应超时 |
|
||||
| `IdleTimeout` | 120s | keep-alive 空闲连接超时 |
|
||||
| `MaxHeaderBytes` | 1MB | 请求头大小限制 |
|
||||
|
||||
Director 改写([proxy.go:36-46](internal/proxy/proxy.go#L36)):剥离 `RemoteAddr` 端口,设置 `X-Forwarded-For` 与 `X-Forwarded-Proto: https`,供 gateway 的 RateLimiter 取真实客户端 IP。
|
||||
|
||||
@@ -263,14 +285,31 @@ ErrorHandler([proxy.go:48-53](internal/proxy/proxy.go#L48)):upstream 不
|
||||
|
||||
## 已知限制
|
||||
|
||||
- 请求体全量读入内存检测,10MB 上限(`SecRequestBodyLimit`)。大文件上传场景需调大或加白名单。
|
||||
- **响应头检测已启用**(`ProcessResponseHeaders`),**响应体检测未启用**(未 `ProcessResponseBody`,缓冲整个响应延迟成本高)。SQL 报错回显等响应体泄漏检测缺失。
|
||||
- 响应头检测(`ProcessResponseHeaders`)当前仅做审计日志,命中后不执行拦截(响应头已发送无法撤销状态码)。如需响应头拦截需引入 buffered response writer。
|
||||
- **响应体检测未启用**(未 `ProcessResponseBody`,缓冲整个响应延迟成本高)。SQL 报错回显等响应体泄漏检测缺失。
|
||||
- 限流/黑白名单未实现,可后续作为 coraza 规则或独立中间件加。
|
||||
- `http.Server` 未设 `ReadTimeout` / `WriteTimeout` / `IdleTimeout`,存在慢连接攻击风险。
|
||||
- TLS 仅设 `MinVersion: TLS1.2`,未限定 cipher suites,允许弱加密套件。
|
||||
- `X-Forwarded-For` 直接覆盖原值,未按 RFC 7239 追加;当前 gateway 也只取第一个 IP,无实际影响,但语义不严格。
|
||||
- `X-Forwarded-For` 直接覆盖原值(`Header.Set`),未按 RFC 7239 追加;当前 gateway 也只取第一个 IP,无实际影响,但语义不严格。注意:IPv6 地址在 X-Forwarded-For 中可能带方括号(待修复)。
|
||||
- CSP 仍含 `'unsafe-inline'`(为兼容 forgot.html 与 games/*.html 的内联脚本),待前端清理后可收紧。
|
||||
- HSTS 已启用(半年期,不含子域),站点全量 HTTPS 覆盖后可按需收紧。
|
||||
- logclient 日志推送每次新建 TCP 短连接,高并发拦截场景可能产生较多 TIME_WAIT;db 地址硬编码 `127.0.0.1:8005`,不支持环境变量配置。
|
||||
- **默认规则全部注释**:`rules/local/custom.conf` 中自定义规则默认注释,需手动取消注释或拉取 OWASP CRS 后才有实际拦截效果。启动时无 warning 提示。
|
||||
- 缺少 SIGINT/SIGTERM 优雅关闭,进程终止时进行中的请求被暴力中断,logclient 队列中未发送的日志可能丢失。
|
||||
- 标准日志(`log.SetOutput`)被静默到 `io.Discard`,net/http 的 panic 堆栈和 TLS 错误不可见。
|
||||
|
||||
## 版本日志
|
||||
|
||||
### v1.1(2026-08-01)— 安全加固
|
||||
|
||||
- **P0 修复:请求体 OOM 防护**:添加 10MB 硬限制,`io.LimitReader` + 多读1字节检测超限,超限返回 413
|
||||
- **P0 修复:Slowloris DoS 防护**:`http.Server` 添加 ReadHeaderTimeout(10s)/ReadTimeout(15s)/WriteTimeout(30s)/IdleTimeout(120s)/MaxHeaderBytes(1MB)
|
||||
- **P1 修复:请求体读取错误处理**:`io.ReadAll` 失败时记录日志并返回,不再传递出错 body 给 ReverseProxy;`ReadRequestBodyFrom`/`ProcessRequestBody` 错误均记录日志
|
||||
- **P2 修复:r.Body 连接复用**:defer 中耗尽并关闭 r.Body,确保 TCP 连接可复用
|
||||
- 413 响应同样注入安全响应头
|
||||
|
||||
### v1.0
|
||||
|
||||
- 初始版本:TLS 终止 + Coraza WAF 规则匹配 + 反向代理 + SIGHUP 热加载 + 异步日志推送 + 安全响应头注入
|
||||
|
||||
## 依赖
|
||||
|
||||
|
||||
Reference in new issue
Block a user