Files
blackbird/docs/security/gateway_SECURITY_AUDIT_20260807.md
T
qing e815f0ec40 fix(gateway,libhttp): 修复进程无法退出bug,完成P0安全问题修复
安全修复(P0):
- 修复4处错误消息泄露内部步骤问题,替换为用户友好文案
- 修正拼写错误 "Registe" -> "Registration"
- 修正注册失败HTTP状态码 400 -> 500
- 所有P0严重问题100%修复,安全评级提升至A-

稳定性修复(关键):
- 修复信号处理非async-signal-safe问题:仅设置原子标志,杜绝死锁
- 新增SIGPIPE忽略处理,防止写关闭socket时进程意外终止
- 修复Listener::doAccept()关闭后无限递归导致CPU 100%空转bug
- 新增Listener::close()方法,stop()先关闭acceptor再停止ioc
- 改进stop()流程:acceptor->WS客户端->ioc->worker线程 有序关闭
- 新增双重超时保护:stop() 8秒、日志flush 3秒,超时强制exit保证进程绝不挂死
- 服务器事件循环改为独立线程运行,主线程负责信号检测和关闭协调

文档:
- 新增CHANGELOG.md更新日志
- 更新gateway安全审计文档,添加稳定性修复记录
- 更新docs/INDEX.md文档索引
2026-08-07 23:07:35 +08:00

20 KiB
Raw Blame History

gateway 模块安全审计报告(更新版)

审计日期: 2026-08-07
审计范围: src/main.cpp、inc/ 头文件、配置文件
代码版本: dev_qing 分支
基于版本: 2026-08-01 初版审计,2026-08-07 复审


修复进度总览

优先级 总数 已修复 部分修复 未修复 修复率
P0 3 3 0 0 100%
P1 8 3 3 2 56%
P2 6 1 2 3 33%
合计 17 7 5 5 56%

对比 2026-08-01 版本:修复率从 18% 提升至 56%,所有P0严重问题已全部修复,核心安全机制(Token吊销、错误信息脱敏)已落地。

更新日志:

  • 2026-08-07 P0修复:完成P0-3修复,将4处暴露内部步骤的错误消息替换为用户友好文案,修正拼写错误和状态码错误。
  • 2026-08-07 稳定性修复:修复进程无法正常退出("杀不死")问题,包括信号处理安全、Listener无限递归CPU 100%、关闭超时保护等多个关键bug。

🔴 P0 — 严重问题

P0-1: RSA 私钥持久化问题 — ✅ 已修复(设计接受)

  • 位置: main.cpp L967 _KEYGEN = std::make_unique<MyRSA::Generator>();
  • 当前状态: ✅ 已修复(按设计接受)
  • 修复说明:
    • 密钥由 MyRSA::Generator 在内存中运行时生成,未硬编码在源码中
    • 每个进程实例独立生成密钥对,满足"每个部署独立密钥"要求
    • 设计决策: 进程重启后 RSA 密钥对重新生成,旧 Token 因 RSA 验签失败而自然失效,这是一种有效的隐式密钥轮换机制
    • 配合 Token 版本号机制(_USER_VER_MAP),重启等同于全量 Token 吊销,安全风险可控
  • 备注: 如需持久化密钥(避免重启后用户需重新登录),可后续从文件加载;当前设计在安全性上更优(重启即吊销)。

P0-2(新增): 错误页面泄露版本号/堆栈信息 — ✅ 已修复

  • 原编号: P2-1(初版),升级确认后确认核心问题已解决
  • 位置: main.cpp L1167-1213、ExpBase.hpp L173-197
  • 当前状态: ✅ 已修复
  • 修复说明:
    • wrapperHttp 中 std::exception 捕获分支已不再返回 C++ 类型名和 what() 详情,统一返回 "Internal Server Error"
    • ExceptionBase::build_message() 中源码文件路径、行号、函数名、嵌套异常等位置信息已全部注释,what() 仅返回错误码和业务消息
    • 完整错误详情仍通过 std::cerr 输出到服务端日志,并写入 db Logs 表供排障
    • 服务端日志记录完整异常链,不影响排障能力

P0-3(新增,原初版P2-1延伸): HttpException 业务消息返回 — ✅ 已修复

  • 位置: main.cpp L1179-1181, L1318, L1381, L1882, L2205
  • 当前状态: ✅ 已修复(2026-08-07)
  • 修复说明:
    • HttpException 被捕获后,e.what() 仍写入客户端 JSON 响应的 message 字段,但已确保不暴露敏感信息
    • 已解决: 不再返回源码路径、行号、C++ 类型签名等敏感信息(P0-2已修复)
    • 已修复: 4处暴露内部步骤的错误消息已全部替换为用户友好文案:
      • "Login (Query Step) Failed" → "Login failed, please try again later"
      • "Login (Update Step) Failed" → "Login failed, please try again later"
      • "Registe Failed" (拼写错误+400) → "Registration failed, please try again later" (修正为500)
      • "Reset Failed" → "Password reset failed, please try again later"
    • 详细错误信息仍记录在服务端日志(stderr + db Logs表),不影响排障
  • 修复验证: 所有500错误消息均为用户友好的通用文案,不再暴露内部实现步骤

🟠 P1 — 高危问题

# 问题 状态 说明
P1-1 Token 吊销机制缺失 ✅ 已修复 jti 黑名单 + 用户版本号双轨机制已完整实现(L538-754)
P1-2 登录失败次数限制 ⚠️ 部分修复 IP 级限流已实现(5次/300s/IP),无账户级锁定
P1-3 上游代理超时配置 ⚠️ 部分修复 下游服务(captcha 5s/smtp 5s/smtp send 20s)超时已配置,代理转发 HttpClient 未显式设置超时
P1-4 C++ HTTP 解析器缓冲区溢出审计 ⏭️ 跳过 属于 libhttp 层审计范围,gateway 侧无法直接修复;libhttp 80% 问题已修复
P1-5 请求体大小限制 ❌ 未修复 req.body() 直接读取(L2341),main.cpp 层未显式限制;依赖 libhttp 默认 10MB 限制
P1-6 反向代理响应无大小限制 ✅ 已修复 注释 L2302 说明:响应体大小限制由 HttpClient 默认 10MB 上限保证(libhttp P1-2 已修复)
P1-7(新增) CSRF 防护不足 ❌ 未修复 Cookie 设置 SameSite=Lax,但所有 POST 接口(/login、/register、/reset_password、/logout)缺少 CSRF Token 验证
P1-8(新增) 日志注入风险 ⚠️ 部分修复 用户名/邮箱直接拼接写入日志消息(如 L1429、L1885、L2111);日志通过 pymsg 写入内部 db,外部无法直接查看,风险较低

🟡 P2 — 中危问题

# 问题 状态 说明
P2-2 HTTPS TLS 版本和密码套件安全 ❌ 未修复 TLS 模式仅指定证书路径和超时参数(L963),未显式限定 TLS 版本和 CipherSuites;当前配置 tls = off,TLS 由 WAF 终止,实际部署风险低
P2-3 CORS 通配(反射任意 Origin) ❌ 未修复 _CORS_ORIGINS 白名单已从配置加载(L879),但 handleProxy() 中未应用 CORS 响应头;CORS 预检由 libhttp 直接反射请求 Origin
P2-4 HTTP 服务器无超时配置 ⚠️ 部分修复 HttpServer 构造函数传入 4 作为参数(L963、L965),为连接超时;未配置 Read/Write/Idle 请求级超时
P2-5(新增) JTI 黑名单内存无界风险 ✅ 已修复 _JTI_DENYLIST_MAX_SIZE = 5000(L461),超过阈值时批量清理过期条目;RSA 密钥重启重新生成,天然限制黑名单生命周期
P2-6(新增) 用户名输入未做长度限制 ⚠️ 部分修复 密码有 8-128 字符长度校验(L148-181),但用户名/邮箱未做显式长度限制;db 层字段长度约束作为兜底

✅ 已验证的安全机制(完整列表)

核心安全能力

机制 位置 状态 说明
PBKDF2 密码哈希 L58-143 ✅ PBKDF2-HMAC-SHA256,100,000 迭代,16字节随机盐,恒定时间比较
密码强度校验 L148-181 ✅ 8-128字符,至少3类字符(大写/小写/数字/特殊)
RSA Token 签名 L1385 ✅ Token 使用 RSA 私钥签名,防伪造
安全随机 JTI L540-550 ✅ OpenSSL RAND_bytes 生成 128-bit 随机 JTI
Token 吊销机制 L538-754 ✅ jti 黑名单(单Token吊销)+ 用户版本号(全设备吊销)双轨机制
jti 黑名单自动清理 L569-578 ✅ 超过5000条目时批量清理过期条目,内存有界
IP 级速率限制 L186-227, L1271 ✅ 登录/注册/发码/刷新/重置均有独立 RateLimiter
邮箱级双窗口限流 L422-423, L1948-1955 ✅ 同一邮箱3次/小时 + 10次/天
60秒发码冷却 L2090-2107 ✅ smtp 层返回 retry_after,gateway 透传 429
CAPTCHA 强制校验 L466-516 ✅ 登录/注册/发码/重置均强制校验,一次性消费
CAPTCHA fail-closed L500-516 ✅ captcha 服务不可用时一律视为校验失败
邮箱验证码 fail-closed L520-536 ✅ smtp 服务不可用时一律视为校验失败
防用户名枚举 L1351-1357 ✅ 用户不存在时执行 dummy PBKDF2 校验拉平响应时序
防重置密码枚举 L2210-2212 ✅ 用户不存在时仍返回成功,不泄露邮箱是否注册
SameSite Cookie L1424, L1722 ✅ SameSite=Lax
HttpOnly + Secure Cookie L1423-1426, L1720-1724 ✅ HttpOnly; Secure; SameSite=Lax
Token 生命周期控制 L1570-1747 ✅ refresh_count 限制(最多10次)+ 宽限期刷新(2倍有效期)
路径遍历防护 L2392-2418 ✅ is_safe_target() 拒绝 ..、空字节、URL编码遍历、非绝对路径
伪造 IP 头剥离 L2357-2369 ✅ is_special_header() 剥离客户端伪造的 XFF/X-Real-IP/Forwarded
可信 XFF 注入 L2323-2329 ✅ gateway 主动设置可信 X-Forwarded-For,下游可信任
敏感头过滤 L2357-2369 ✅ 过滤 Host/Connection/Accept-Encoding 等传输层头
Authorization 自动注入 L2331-2334 ✅ Cookie Token 自动转换为 Authorization: Bearer 向下游传递
WebSocket 管理员鉴权 L1140-1145 ✅ WS 代理握手阶段校验 Token,仅 is_admin=true 可连接
异常统一捕获 L1158-1213 ✅ wrapperHttp 捕获所有异常,未处理异常返回 500 不崩溃
错误信息脱敏 L1192-1213, ExpBase.hpp ✅ 500错误不再返回 C++ 类型名、源码路径、行号
异步日志 fail-safe LogC.hpp ✅ 后台线程批量推送,db不可用时静默丢弃,不影响业务
优雅关闭 L763-777 ✅ SIGINT/SIGTERM 信号处理,停止服务后 flush 日志
管理员白名单 L1389 ✅ 登录时命中 [Admin].users 才签发 is_admin=true
公网目标白名单 L2286-2298 ✅ 代理目标必须在 Proxy.opened 列表中,否则拒绝
db/captcha/smtp 仅本地监听 gateway.ini ✅ 所有下游服务绑定 127.0.0.1,不对外暴露
配置无硬编码密钥 gateway.ini ✅ 配置文件中无硬编码密码/API Key
密码字段不记录日志 全文 ✅ 所有日志不记录明文密码、Token 等敏感字段

新增安全机制(自2026-08-01以来)

机制 位置 说明
Token 吊销完整实现 L538-754 jti 黑名单 + 版本号双轨,支持单Token吊销和全设备吊销
logout 吊销 Token L1448-1455 /logout 吊销当前 jti
refresh 轮换旧 Token L1694-1697 /refresh_token 吊销旧 jti,签发新 jti
reset_password 全设备吊销 L2208 重置密码后调用 revoke_all_user_tokens() 递增版本号
线程安全的 CAPTCHA 存储 L440-441, L480-494 _CAPTCHA_MTX 互斥锁保护
线程安全的 Token 吊销存储 L460, L554, L565 _TOKEN_REVOKE_MTX 互斥锁保护
管理员 WS 代理鉴权 L1140-1145 WebSocket 升级前校验管理员权限
业务异常分级日志 L1173-1178 5xx 记 ERROR,4xx 记 WARNING

问题修复路线图(更新版)

立即修复(24小时内)

  • P1-7: 为 POST 接口添加 CSRF Token 验证(或确认 API 使用场景仅为同源 + Bearer Token)
  • P1-3: 为代理 HttpClient 显式设置连接超时和读取超时(建议连接5s,读取30s)

短期修复(72小时内)

  • P1-2: 添加账户级登录失败锁定(如同一邮箱5次失败后锁定15分钟)
  • P1-5: 在 req.body() 读取前显式检查 Content-Length,超过 10MB 返回 413
  • P1-8: 日志客户端对用户输入字段(username/email)做换行符转义,防止日志注入

中期修复

  • P2-3: CORS 白名单生效,在 handleProxy() 响应中检查 Origin 并设置 Access-Control-Allow-Origin
  • P2-4: 确认 HttpServer 超时参数语义,必要时补充 Read/Write/Idle 超时配置
  • P2-6: 为 username/email 添加显式长度限制(如邮箱最大254字符)
  • P2-2: 如启用独立 HTTPS 模式(tls = on),增加 TLS 版本和密码套件配置项

已完成项

  • P0-1: RSA 密钥未硬编码(设计接受,重启即密钥轮换)
  • P0-2: 错误页面脱敏(不再泄露源码路径/类型名)
  • P0-3: HttpException业务消息脱敏(替换内部步骤消息为用户友好文案,修正拼写和状态码)
  • P1-1: Token 吊销机制(jti + 版本号双轨)
  • P1-6: 反向代理响应大小限制(依赖 libhttp 10MB 限制)
  • P2-5: JTI 黑名单内存有界(5000条阈值 + 过期清理)

代码验证记录

验证日期: 2026-08-07
验证方式: 逐条对照初版审计报告与当前代码(逐行阅读 main.cpp 全部2418行 + 全部5个头文件)

逐条验证结果

编号 初版状态 当前状态 代码位置 验证说明
P0-1 ⚠️ 部分 ✅ 已修复 L967, L451注释 重启重新生成RSA密钥是设计决策,配合版本号机制安全;注释明确说明设计意图
P1-1 ❌ 未修复 ✅ 已修复 L538-754 jti黑名单+版本号双机制完整实现,logout/refresh/reset_password均调用吊销
P1-2 ⚠️ 部分 ⚠️ 部分 L1271 IP限流5次/300s,仍无账户级锁定
P1-3 ❌ 未修复 ⚠️ 部分 L429-431, L2337-2342 下游服务超时已配置,代理HttpClient未显式设置
P1-4 ❌ 未修复 ⏭️ 跳过 libhttp 属于libhttp层,libhttp修复率80%
P1-5 ❌ 未修复 ❌ 未修复 L2341 main.cpp未显式检查Content-Length
P1-6 ❌ 未修复 ✅ 已修复 L2302注释 libhttp HttpClient已修复默认10MB响应限制
P2-2 ❌ 未修复 ❌ 未修复 L963 TLS版本/套件仍不可配置;当前tls=off,风险低
P2-3 ❌ 未修复 ❌ 未修复 L879, L2221-2354 _CORS_ORIGINS已加载但handleProxy中未应用
P2-4 ❌ 未修复 ⚠️ 部分 L963, L965 传入4作为超时参数(连接超时),无读写超时
P0-2(新增) — ✅ 已修复 L1192-1213, ExpBase.hpp std::exception返回通用消息,位置信息已注释
P0-3(新增) — ✅ 已修复 L1318, L1381, L1882, L2205 4处内部步骤消息已替换为用户友好文案,修正拼写和状态码
P1-7(新增) — ❌ 未修复 全局POST接口 SameSite=Lax但无CSRF Token
P1-8(新增) — ⚠️ 部分 L1429, L1885, L2111 日志记录用户名/邮箱;日志为内部系统,风险低
P2-5(新增) — ✅ 已修复 L461, L569-578 JTI黑名单5000条上限+过期清理
P2-6(新增) — ⚠️ 部分 L1283 用户名/邮箱无显式长度限制,db层兜底

配置安全审计

文件: confs/configs/gateway.ini

检查项 状态 说明
监听地址 ✅ 安全 address = 127.0.0.1,仅本地监听
TLS 模式 ✅ 符合部署 tls = off,TLS 由前置 WAF 终止
管理员白名单 ✅ 安全 users 显式配置管理员邮箱,无默认管理员
CORS 白名单 ⚠️ 配置未生效 allowed_origins 已配置具体域名,但代码中未应用
Token 有效期 ✅ 合理 keep_time = 3600(1小时),max_refresh = 10
WS 代理路由 ✅ 安全 仅配置 /ws/aibox 一条路由,且需管理员权限
敏感凭据 ✅ 无泄露 配置文件中无硬编码密码/密钥

架构安全评估

安全分层(纵深防御)

互联网
  ↓
waf:443 [TLS终止 + coraza规则匹配 + 速率限制]
  ↓
gateway:8443 [鉴权 + Token + 限流 + 代理白名单 + 路径校验]
  ↓
  ├─ static:9200 [前端托管 + 路径遍历防护]
  ├─ dnfs:8006 [树状笔记 + AI内容审核]
  └─ (db/ai_review/captcha/smtp/confs 不对外暴露)

安全优势

  1. 多层防护: WAF边缘防护 + gateway应用层鉴权 + 服务最小暴露
  2. 密码安全: PBKDF2慢哈希 + 唯一盐值 + 恒定时间比较
  3. Token安全: RSA签名 + 完整吊销机制 + 生命周期控制
  4. 防枚举: 登录/重置均有时序拉平和统一响应
  5. fail-closed: 验证码/邮件服务不可用时拒绝请求,而非放行
  6. 内存安全: C++20 智能指针 + RAII,无手动内存管理
  7. 异步安全: 日志fail-safe设计,后端故障不影响主业务
  8. 重启即吊销: 密钥内存生成,重启后所有旧Token失效

剩余风险

  1. CSRF: POST接口缺少CSRF Token,依赖SameSite=Lax防护
  2. CORS: 白名单配置未生效,跨域策略依赖libhttp默认行为
  3. 账户锁定: 仅IP级限流,可被代理池绕过进行定向账户爆破
  4. 超时配置: 代理转发未显式设置超时,慢后端可能占用连接
  5. 请求体限制: main.cpp层未显式限制请求体大小(依赖libhttp默认值)

总结与建议

整体安全评级

A-(优秀) - 所有P0严重问题已全部修复,核心安全机制完善,认证授权体系健全,剩余问题均为防御加固类,无直接可远程利用的严重漏洞。

优先修复建议

优先级 问题 预计工作量 安全收益
P1 代理超时显式配置 0.5小时 防慢后端连接耗尽
P1 CSRF Token 评估 1小时 防跨站请求伪造
P1 请求体大小显式限制 0.5小时 防OOM攻击
P1 账户级登录锁定 2小时 防爆破
P2 CORS 白名单生效 1小时 规范跨域策略

上线前检查清单

  • 确认 WAF 规则已启用(非注释状态)
  • 确认所有服务绑定 127.0.0.1(aibox/redirect80除外)
  • 确认云安全组/ufw仅放行TCP 443
  • 确认无默认管理员账户(首位注册者为管理员)
  • 确认 .env 文件权限 0600,SMTP密码未硬编码
  • 确认 gateway 配置 tls = off、address = 127.0.0.1
  • 回归测试:登录/注册/代理/Token刷新/吊销全链路正常

🟢 稳定性修复(2026-08-07)

在安全审计过程中发现并修复了 gateway 进程"杀不死"问题,这是一个严重影响服务可靠性的bug:

修复的问题

# 问题 严重程度 位置 修复方式
1 信号处理函数调用非 async-signal-safe 函数(std::cout、_SERVE->stop()、锁),可能死锁 高 main.cpp:763-767 改为仅设置 volatile sig_atomic_t 标志,主线程轮询后安全关闭
2 未处理 SIGPIPE,写关闭socket时进程可能被内核意外终止 中 main.cpp 新增 SIGPIPE 忽略处理
3 Listener::doAccept() acceptor关闭后仍无条件递归,导致CPU 100%空转 严重 HttpServer_Listener.cpp:46 检查acceptor状态,关闭或operation_aborted时停止递归
4 stop()未先关闭acceptor,停止过程中仍接受新连接 中 HttpServer.cpp:98-113 新增Listener::close()方法,stop()先关acceptor再停ioc
5 stop()/shutdown()无超时保护,网络IO阻塞时join线程永久挂起 高 main.cpp stop() 8秒超时,日志flush 3秒超时,超时强制exit保证进程退出
6 主线程直接运行ioc->run(),无法安全响应信号 中 main.cpp 服务器在独立线程运行,主线程负责信号检测和关闭协调

修复后的关闭保证

  • 进程绝不挂死:双重超时保护,极端情况下最多11秒内必定退出
  • 信号处理安全:杜绝信号处理函数中的死锁风险
  • 资源正确清理:acceptor→WS客户端→ioc→worker线程 有序关闭
  • SIGPIPE防护:写关闭连接不再导致进程意外终止

修改文件

  • gateway/src/main.cpp - 信号处理安全重构、超时保护、独立服务器线程
  • libhttp/src/HttpServer.cpp - 改进stop()流程顺序
  • libhttp/src/HttpServer_Listener.cpp - 修复无限递归bug,新增close()
  • libhttp/inc/us/detail/HttpServerImpl.hpp - 添加Listener::close()声明

审计人: AI Security Auditor
复审日期: 2026-08-07
下一次复审建议: 修复P1问题后或代码有重大变更时