docs(security): 新增 libmsg 安全审计报告,更新文档索引

新增 docs/security/libmsg_SECURITY_AUDIT_20260806.md,记录 SAST
扫描发现的 3 个漏洞(CWE-362/400/209)及对应修复方案,更新
docs/INDEX.md 加入 libmsg 审计文档入口。
This commit is contained in:
qing committed 2026-08-06 17:18:27 +00:00
1 parent e92ef05ed0
commit a4611ec737
2 files changed
+152

No files matched your search

+1
View File
@@ -47,6 +47,7 @@ docs/
| smtp | [smtp_SECURITY_AUDIT_20260801.md](security/smtp_SECURITY_AUDIT_20260801.md) | Python 邮件服务(验证码发送/校验) |
| captcha | [captcha_SECURITY_AUDIT_20260801.md](security/captcha_SECURITY_AUDIT_20260801.md) | Python 验证码服务 |
| confs | [confs_SECURITY_AUDIT_20260801.md](security/confs_SECURITY_AUDIT_20260801.md) | Python 统一配置服务 |
| libmsg | [libmsg_SECURITY_AUDIT_20260806.md](security/libmsg_SECURITY_AUDIT_20260806.md) | C++ RPC 基础库(pymsg 协议帧、MsgClient/Server、RpcClient/Server) |
| libhttp | [libhttp_SECURITY_AUDIT_20260803.md](security/libhttp_SECURITY_AUDIT_20260803.md) | C++ HTTP/WebSocket 库(请求/响应/WS/静态文件) |
| aibox | [aibox_SECURITY_AUDIT_20260801.md](security/aibox_SECURITY_AUDIT_20260801.md) | Python AI 智能体系统(WebSocket) |
| BotOps | [BotOps_SECURITY_AUDIT_20260801.md](security/BotOps_SECURITY_AUDIT_20260801.md) | BotOps 运维控制面板 |
@@ -0,0 +1,151 @@
# libmsg 模块安全审计报告
**审计日期**: 2026-08-06
**审计范围**: `libmsg/inc/us/MsgServer.hpp`、`libmsg/inc/us/RpcServer.hpp`、`libmsg/inc/us/Msg.hpp`、`libmsg/inc/us/RpcClient.hpp`、`libmsg/inc/us/AsyncQueue.hpp`、`libmsg/inc/us/Msg_Basic.hpp`
**代码版本**: main 分支(e92ef05)
**审计工具**: Qoder Security SAST 云端扫描
---
## 问题严重等级
| 等级 | 说明 | 响应时间 |
|------|------|----------|
| P0 🔴 | 可导致服务崩溃、DoS 或远程接管 | 立即修复 |
| P1 🟠 | 可导致资源耗尽、信息泄露 | 72小时内 |
| P2 🟡 | 健壮性/配置问题 | 一周内 |
| P3 🟢 | 优化建议 | 版本迭代 |
---
## 修复进度汇总
| 等级 | 总数 | 已修复 | 待修复 | 修复率 |
|------|------|--------|--------|--------|
| P0 | 1 | 1 | 0 | 100% |
| P1 | 1 | 1 | 0 | 100% |
| P2 | 1 | 1 | 0 | 100% |
| **合计** | **3** | **3** | **0** | **100%** |
---
## 🔴 P0 — 严重问题
### P0-1: broadcast/reply 竞态条件导致数据发送到错误客户端 ✅ 已修复
- **CWE**: CWE-362 (Race Condition)
- **OWASP**: A01:2021
- **位置**: `MsgServer.hpp:228`(原始行号)
- **置信度**: 80%
**问题描述**:
`broadcast()` 在 `_clients_mtx` 保护下拷贝 fd + send_lock 列表后释放锁,再无锁执行 `pmsg_send(fd, msg)`。此窗口期内 `_remove_client()` 可关闭该 fd,OS 可将其分配给新连接。`broadcast()`/`reply()` 随后向复用的 fd 发送数据,导致消息到达错误的客户端。`_remove_client()` 原实现关闭 fd 前不获取 per-connection `send_lock`,因此 `send_lock` 无法阻止此竞态。
**数据流**:
1. `broadcast()` 在 `_clients_mtx` 下拷贝 fd 和 send_lock 列表
2. `_remove_client()` 关闭 fd 但不持 send_lock,OS 复用该 fd
3. `broadcast()` 向已被复用的 fd 调用 `pmsg_send()`,数据到达新连接
**修复方案**(commit `e92ef05`):
`_remove_client()` 先从 `_send_locks` 取出 `send_lock` 并从 map 删除,持有 `send_lock` 后再执行 `::close(fd)`,确保关闭与正在进行的发送操作互斥:
```cpp
void _remove_client(int fd) {
std::shared_ptr<std::mutex> send_lock;
{
std::lock_guard<std::mutex> clk(_clients_mtx);
if (_clients.erase(fd) == 0) return;
auto it = _send_locks.find(fd);
if (it != _send_locks.end()) {
send_lock = it->second;
_send_locks.erase(it);
}
}
if (send_lock) {
std::lock_guard<std::mutex> slk(*send_lock);
::close(fd);
} else {
::close(fd);
}
}
```
---
## 🟠 P1 — 高危问题
### P1-1: 事件循环顺序阻塞接收导致 DoS(Slowloris 变体)✅ 已修复
- **CWE**: CWE-400 (Uncontrolled Resource Consumption)
- **OWASP**: A05:2021
- **位置**: `MsgServer.hpp:424`(原始行号)
- **置信度**: 80%
**问题描述**:
`_event_loop()` 对每个就绪 fd 顺序调用 `_handle_client()`,后者在**事件循环主线程**中阻塞调用 `pmsg_recv(fd, recv_timeout)`(默认 10 秒)。线程池仅处理回调执行,不处理消息接收,即使配置了 `workers > 0` 也无法缓解。攻击者用 `max_connections`(默认 1000)个连接各发送不完整帧头(如 3 字节),可使事件循环阻塞最长 `1000 × 10 = 10000` 秒(约 2.8 小时),期间服务完全无法处理任何新连接或已有消息。
**数据流**:
1. 事件循环顺序遍历所有就绪的客户端 fd
2. `_handle_client()` 在主线程调用 `pmsg_recv(fd, recv_timeout)`,阻塞最长 `recv_timeout` 秒
**修复方案**(commit `e92ef05`):
在 `workers > 0` 模式下,`_handle_client()` 不再在事件循环线程调用 `pmsg_recv()`,而是提交 `needs_recv=true` 的任务给 worker 线程,由 worker 线程负责完整的 recv + callback 流程:
```cpp
// 线程池模式:直接提交,由 worker 负责 recv + callback
_pool_queue.push(Task{fd, std::move(addr), {}, true});
```
同步模式(`workers=0`)行为不变,仍在主线程顺序处理(无线程池时无法并发)。
---
## 🟡 P2 — 中危问题
### P2-1: 错误信息路径脱敏正则不完整,可能泄露内部路径 ✅ 已修复
- **CWE**: CWE-209 (Information Exposure Through an Error Message)
- **OWASP**: A04:2021
- **位置**: `RpcServer.hpp:184`(原始行号)
- **置信度**: 75%
**问题描述**:
`RpcServer::_sanitize_error()` 使用正则 `/[A-Za-z0-9_.\-]+(?:/[A-Za-z0-9_.\-]+)*` 替换异常消息中的绝对路径,但该正则仅匹配由 `[A-Za-z0-9_.\-]` 组成的路径段。含空格(`/home/user name/`)、括号(`/tmp/file(1).txt`)、`@`(`/opt/app@2.0/`)、`~` 等特殊字符的路径不被匹配,会原文返回给远程客户端,泄露内部服务器路径信息。
**修复方案**(commit `e92ef05`):
将正则扩展为 `/\S+(?:/\S*)*`,匹配以 `/` 开头、后接所有非空白字符的路径段,覆盖所有常见内部路径格式:
```cpp
static const std::regex path_re(R"(/\S+(?:/\S*)*)");
return std::regex_replace(msg, path_re, "<redacted>");
```
---
## ✅ 已有安全机制
| 机制 | 位置 | 说明 |
|------|------|------|
| 消息大小上限 64 MiB | `Msg_Basic.hpp` `kMaxMsgSize` | 防止超大消息耗尽内存 |
| deadline 模式 recv 超时 | `Msg.hpp` `pmsg_recv(fd, timeout)` | header + payload 共享总超时,防 Slowloris |
| 最大连接数限制 | `MsgServer.hpp` `max_connections=1000` | 超限拒绝新连接 |
| 有界任务队列 | `MsgServer.hpp` `max_queue_size=1000` | 队列满时断开连接,防 OOM |
| per-connection 发送锁 | `MsgServer.hpp` `_send_locks` | reply/broadcast 线程安全,防帧交错 |
| 同一连接消息串行 | `MsgServer.hpp` `_busy` 集合 | 回调执行中暂停该连接 select 监听 |
| JSON 解析深度限制 | `RpcServer.hpp` `opts.max_depth=32` | 防深度嵌套 JSON 导致栈溢出 |
| 异常信息脱敏 | `RpcServer.hpp` `_sanitize_error()` | 过滤绝对路径,完整异常由 `on_error` 钩子记录 |
| RpcClient 响应格式校验 | `RpcClient.hpp` | 校验 `success` 字段,非法响应抛 `RpcError` |
| worker 线程异常隔离 | `MsgServer.hpp` `_worker_loop()` | handler 异常仅断开该连接,不影响服务 |
| select FD_SETSIZE 检查 | `MsgServer.hpp` `_accept_client()` | fd >= FD_SETSIZE 时拒绝连接,防 select 越界 |
---
## 待修复 / 已知限制
| # | 问题 | 优先级 | 说明 |
|---|------|--------|------|
| L-1 | 无认证/鉴权机制 | P0(已缓解) | 仅依赖绑定 127.0.0.1 网络层隔离,任意本地进程可调用所有路由 |
| L-2 | 无 TLS 加密传输 | P1 | 内网明文 TCP,进程间通信可被本地抓包 |
| L-3 | 同步模式(workers=0)仍在主线程阻塞 recv | P2 | 无线程池时无法并发,Slowloris 防护依赖 recv_timeout 截止时间 |
| L-4 | select 模型上限 FD_SETSIZE(通常 1024) | P2 | 高并发场景建议改用 epoll |
| L-5 | 安全事件无结构化日志 | P3 | 超时、超大消息、JSON 解析失败等仅 fprintf(stderr),无可观测性 |