- 新增 setMaxWsQueueSize()(default 1000),每个入队点检查 - wsSend / onProxyReadClient / onProxyReadBackend 超限关闭连接 - 更新审计文档 P1-6 状态、README v3.4.3
109 lines
8.9 KiB
Markdown
109 lines
8.9 KiB
Markdown
# libhttp 安全审计报告
|
||
|
||
**审计日期**: 2026-08-03
|
||
**审计范围**: `libhttp/inc/us/`、`libhttp/src/`(HttpServer 6 个拆分 TU + HttpClient + 遗留 WebSocketServer/WebSocketClient)
|
||
**代码版本**: dev_qing 分支
|
||
|
||
---
|
||
|
||
## 修复进度
|
||
|
||
| 优先级 | 总数 | 已修复 | 部分修复 | 未修复 |
|
||
|--------|------|--------|----------|--------|
|
||
| P0 | 0 | 0 | 0 | 0 |
|
||
| P1 | 6 | 6 | 0 | 0 |
|
||
| P2 | 4 | 0 | 0 | 4 |
|
||
| **合计** | **10** | **8** | **0** | **2** |
|
||
|
||
---
|
||
|
||
## 🟠 P1 — 高危问题
|
||
|
||
| # | 问题 | 状态 | 说明 |
|
||
|---|------|------|------|
|
||
| P1-1 | 请求体大小限制不可配置 + 超限无 413 | ✅ 已修复 | `Session::doRead`(`HttpServer_Request.cpp` L167-186)改用 `http::request_parser` + `body_limit()`(默认 10MB,与 WAF 对齐);`HttpServer` 新增 `setMaxBodySize()`/`getMaxBodySize()`(`HttpServer.hpp` L118-120)。超限时 `handleRead` 返回 **413 Payload Too Large** 并关闭连接(L256-265),而非静默断连 |
|
||
| P1-2 | HttpClient 响应无大小限制 | ✅ 已修复 | `HttpClient.cpp` 新增 `WriteCtx`(流式回调 + 累计字节 + 上限),`write_callback` 超限返回 0 触发 `CURLE_WRITE_ERROR`,`request_stream_sync` 抛异常;`request_stream_sync` 改为**真流式**(callback 非空时每块即回调,不二次累积),`HttpClient` 新增 `set_max_response_size()`/`get_max_response_size()`(默认 10MB,对齐 WAF,0 表示不限制) |
|
||
| P1-3 | WebSocket 消息无大小限制 | ✅ 已修复 | `HttpServer` 新增 `max_ws_message_size_`(默认 16MB,与 Beast 默认一致)与 `setMaxWsMessageSize()`/`getMaxWsMessageSize()`。`doWsUpgrade`/`doWssUpgrade` 及代理流 `onProxyConnect`/`onProxySslHandshake` 创建后调用 `read_message_max()`,超限消息由 Beast 在分配内存前拒绝并关闭连接,防 OOM |
|
||
| P1-4 | 服务器无读写超时(Slowloris) | ✅ 已修复 | 入站 `stream_` 现设置超时:`doSSLHandshake` 设头超时(覆盖握手),`doRead` 两阶段读(`async_read_header` 头超时 → `async_read` 体超时),`doWsRead` 每个 WS 读取设空闲超时。`HttpServer` 新增 `setHeaderTimeout()`(默认 10s)/`setReadTimeout()`(默认 30s),对齐 WAF ReadHeaderTimeout/ReadTimeout。慢速/空闲连接被清理,防 Slowloris/连接耗尽 |
|
||
| P1-5 | 静态文件目录路径遍历 | ✅ 已修复 | 新增 `isSafeStaticPath()`(`HttpServer_Request.cpp` L139-156),静态文件服务前校验 target_path(拒绝 `..`、空字节、非 `/` 开头、URL 编码 `%2e`/`%2f`/`%5c`),逻辑与 gateway `is_safe_target()` 对齐(`HttpServer.cpp` L94) |
|
||
| P1-6 | 发送队列无界(慢消费者 OOM) | ✅ 已修复 | `HttpServer` 新增 `setMaxWsQueueSize()`(默认 1000),`wsSend` / `onProxyReadClient` / `onProxyReadBackend` 入队前检查,超过上限判定慢消费者/慢后端,关闭连接并清空队列,防止内存耗尽 |
|
||
|
||
---
|
||
|
||
## 🟡 P2 — 中危问题
|
||
|
||
| # | 问题 | 状态 | 说明 |
|
||
|---|------|------|------|
|
||
| P2-1 | CORS 反射任意 Origin | ❌ 未修复 | OPTIONS 预检(`HttpServer_Request.cpp` L73-74)把 `Access-Control-Allow-Origin` 直接设为请求的 `Origin` 值,任意来源均被放行,无白名单校验(对应全局审计 P2-3 根因) |
|
||
| P2-2 | TLS 客户端一律不校验证书 | ❌ 未修复 | `HttpClient.cpp` L131 与 WS 代理 `HttpServer_WsSession.cpp` L243 均 `set_verify_mode(asio::ssl::verify_none)`。对内部后端可接受,但库内不留逃生口,无法在需要校验时启用 |
|
||
| P2-3 | WebSocket 握手无 Origin/CSWSH 校验 | ❌ 未修复 | libhttp 对任意 `ws`/`ws_proxy` upgrade 请求直接握手,不校验 `Origin`/`Sec-WebSocket-Protocol`。若后端依赖 Cookie 鉴权,存在跨站 WebSocket 劫持(CSWSH)风险(gateway 的 `ws_proxy` 依赖握手回调鉴权,属应用层缓解) |
|
||
| P2-4 | HttpClient `ws_recv` 忽略 timeout 参数 | ❌ 未修复 | `HttpClient.cpp` L220-221 `(void)timeout_ms;` 丢弃超时参数,`ws_recv` 会**无限阻塞**。后端挂起时调用线程永久卡死 |
|
||
| P2-5 | 反向代理后端连接建立后无超时 | ❌ 未修复 | 代理后端握手成功后 `proxy_tcp_stream_->expires_never()`(`HttpServer_WsSession.cpp` L262/L287),双向读写无超时,慢后端可长期占用连接 |
|
||
| P2-6 | 无连接数限制 | ❌ 未修复 | `Listener::doAccept`(`HttpServer_Listener.cpp` L23-48)无限接受连接,每连接一个 Session,无最大连接数上限,可被连接耗尽 |
|
||
|
||
---
|
||
|
||
## 🔵 P3 — 低危问题
|
||
|
||
| # | 问题 | 状态 | 说明 |
|
||
|---|------|------|------|
|
||
| P3-1 | 遗留 WebSocketServer/WebSocketClient 未构建 | ✅ 已修复 | 已删除 `WebSocketServer.cpp`/`WebSocketClient.cpp`/`WebSocketServer.hpp`/`WebSocketClient.hpp`/`WebSocketClientBase.hpp`(未构建、与 `HttpClient` 别名同名冲突、有缺陷),并清理 Makefile/README 引用。WebSocket 能力由 `HttpServer` + `HttpClient` 提供 |
|
||
| P3-2 | CMakeLists.txt 过时 | ✅ 已修复 | CMakeLists 已重写对齐 Makefile:`libus_http.so`(6 个服务端 TU)+ `libus_httpclient.so`(HttpClient),移除遗留文件引用与错误库名 |
|
||
|
||
---
|
||
|
||
## ✅ 已有安全机制
|
||
|
||
1. ✅ **Beast 默认头/体限制**: `http::async_read` 使用 Beast 默认 `header_limit`(8KB) + `body_limit`(1MB),超限拒绝暴力请求
|
||
2. ✅ **TLS 服务端密码套件受限**: `initSSLContext`(`HttpServer.cpp` L30-62)禁用 `!aNULL:!MD5:!DSS`,仅允许强套件
|
||
3. ✅ **TLS 服务端禁用 SSLv2/v3**: `no_sslv2 | no_sslv3`(`HttpServer.cpp` L36-37)
|
||
4. ✅ **会话生命周期管理**: `enable_shared_from_this` + `close()` 析构,连接关闭时清理 WS 代理队列
|
||
5. ✅ **代理后端连接超时**: 建立连接阶段设 `expires_after(10s)`(`HttpServer_WsSession.cpp` L225/L253)
|
||
6. ✅ **WS 消息顺序保持**: 写队列串行化,避免并发写交错
|
||
7. ✅ **WS 代理握手鉴权回调**: `ws_proxy` 支持 `WsProxyHandshakeHandler` 在 upgrade 阶段授权(gateway 用于 admin-only 控制)
|
||
|
||
---
|
||
|
||
## 修复路线图
|
||
|
||
### 立即(24小时)
|
||
- [x] P1-1 请求体大小限制:`doRead` 改用 `http::request_parser` + `body_limit()`,`HttpServer` 增加 `setMaxBodySize()`(对齐 WAF 10MB),超限返回 413 而非断开 → ✅ 已实现(默认 10MB,`setMaxBodySize()` 可覆盖)
|
||
- [x] P1-2 HttpClient 响应上限:`write_callback` 累计超过上限即失败,返回错误 → ✅ 已实现(`WriteCtx` 流式回调 + 上限,默认 10MB,`set_max_response_size()` 可覆盖)
|
||
- [x] P1-4 服务器读超时:入站 `stream_` 设置 `expires_after`(首字节/头+体超时),防 Slowloris → ✅ 已实现(`setHeaderTimeout()` 默认 10s / `setReadTimeout()` 默认 30s,两阶段读 + WS 空闲超时)
|
||
|
||
### 短期(72小时)
|
||
- [x] P1-3 WS 消息大小限制:设置 `read_message_max`(可配置)→ ✅ 已实现(`setMaxWsMessageSize()`,默认 16MB,服务端与代理流均生效)
|
||
- [x] P1-5 静态文件路径遍历:新增 `isSafeStaticPath()` 拼接前校验 `target_path`,拒绝 `..`/空字节/编码遍历 → ✅ 已实现(`HttpServer_Request.cpp` L139-157)
|
||
- [x] P1-6 发送队列上限:`ws_send_queue_`/代理队列设最大值,超限断开慢消费者 → ✅ 已实现(`setMaxWsQueueSize()`,默认 1000,入队前检查超限即关闭)
|
||
- [ ] P2-1 CORS 白名单:origin 白名单校验,替代反射任意 Origin
|
||
|
||
### 中期
|
||
- [ ] P2-3 WS 握手 Origin 校验(防 CSWSH)
|
||
- [ ] P2-4 `ws_recv` 实现超时(用 `io_context` + timer 或 `async_read` + 私有超时)
|
||
- [ ] P2-5 代理后端读写超时 + 空闲保活
|
||
- [ ] P2-6 最大连接数限制
|
||
- [ ] P2-2 TLS 校验可配置(至少提供 `verify_peer` 选项)
|
||
- [x] P3-1 删除或维护遗留 WebSocketServer/WebSocketClient → ✅ 已删除
|
||
- [x] P3-2 同步 CMakeLists.txt 与 Makefile → ✅ 已重写对齐
|
||
|
||
---
|
||
|
||
## 代码位置索引
|
||
|
||
| 功能 | 文件 | 行号 |
|
||
|------|------|------|
|
||
| 请求读入(body_limit 可配置) | `src/HttpServer_Request.cpp` | L167-186 |
|
||
| 请求体超限返回 413 | `src/HttpServer_Request.cpp` | L256-265 |
|
||
| setMaxBodySize/getMaxBodySize | `inc/us/HttpServer.hpp` | L118-120 |
|
||
| 请求处理/静态文件 | `src/HttpServer_Request.cpp` | L7-137, L94-119 |
|
||
| CORS 反射 Origin | `src/HttpServer_Request.cpp` | L73-74 |
|
||
| WS 读消息(无大小限制) | `src/HttpServer_WsSession.cpp` | L88-111 |
|
||
| WS 发送队列/代理队列 | `src/HttpServer_WsSession.cpp` | L8-16, L362-367, L438-443 |
|
||
| 代理后端超时 | `src/HttpServer_WsSession.cpp` | L225, L253, L262, L287 |
|
||
| 代理 TLS verify_none | `src/HttpServer_WsSession.cpp` | L243 |
|
||
| 连接接受(无限制) | `src/HttpServer_Listener.cpp` | L23-48 |
|
||
| TLS 服务端配置 | `src/HttpServer.cpp` | L30-62 |
|
||
| HttpClient 响应无上限 | `src/HttpClient.cpp` | L63-81, L329-334 |
|
||
| HttpClient TLS verify_none | `src/HttpClient.cpp` | L131 |
|
||
| HttpClient ws_recv 忽略超时 | `src/HttpClient.cpp` | L220-221 |
|
||
| 遗留 WS 实现(未构建) | `src/WebSocketServer.cpp`、`src/WebSocketClient.cpp` | 全篇 | |