Files
blackbird/docs/security/libmsg_SECURITY_AUDIT_20260806.md
T
qing a4611ec737 docs(security): 新增 libmsg 安全审计报告,更新文档索引
新增 docs/security/libmsg_SECURITY_AUDIT_20260806.md,记录 SAST
扫描发现的 3 个漏洞(CWE-362/400/209)及对应修复方案,更新
docs/INDEX.md 加入 libmsg 审计文档入口。
2026-08-06 17:18:27 +00:00

6.9 KiB
Raw Blame History

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),确保关闭与正在进行的发送操作互斥:

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 流程:

// 线程池模式:直接提交,由 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*)*,匹配以 / 开头、后接所有非空白字符的路径段,覆盖所有常见内部路径格式:

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),无可观测性