diff --git a/docs/security/DEPS_SECURITY_AUDIT_20260801.md b/docs/security/DEPS_SECURITY_AUDIT_20260801.md index 9c7b7b5..d181145 100644 --- a/docs/security/DEPS_SECURITY_AUDIT_20260801.md +++ b/docs/security/DEPS_SECURITY_AUDIT_20260801.md @@ -35,7 +35,7 @@ | # | 问题 | 位置 | 说明 | |---|------|------|------| | P1-1 | libcrypt RSA 实现需审计 | `libcrypt/src/Crypto_Basic.cpp`、`inc/us/MyRSA.hpp` | 自研 RSA 实现需验证侧信道攻击防护、填充方案(PKCS#1 v1.5/OAEP)正确性 | -| P1-2 | WebSocket 帧解析需 fuzz | `libhttp/src/WebSocketServer.cpp` | 畸形帧、超大帧、分片帧处理需验证 | +| P1-2 | WebSocket 帧解析需 fuzz | ⚠️ 原文件已删除 | `libhttp/src/WebSocketServer.cpp` 已删除(见 libhttp 审计 P3-1),WebSocket 能力现由 `HttpServer` + `HttpClient` 提供,帧解析 fuzz 需求转移至 `HttpServer_WsSession.cpp` | --- @@ -60,7 +60,7 @@ ## 修复路线图 ### 短期(72小时) -- [ ] 对 libhttp HTTP/WebSocket 解析器进行 fuzz 测试 +- [ ] 对 libhttp HTTP/WebSocket 解析器进行 fuzz 测试(WebSocket 部分针对 `HttpServer_WsSession.cpp`) - [ ] 验证 libsqlite3 封装的参数化查询正确使用 ### 中期 diff --git a/docs/security/SECURITY_HARDENING_PLAN_20260803.md b/docs/security/SECURITY_HARDENING_PLAN_20260803.md index 3d5af8d..515af09 100644 --- a/docs/security/SECURITY_HARDENING_PLAN_20260803.md +++ b/docs/security/SECURITY_HARDENING_PLAN_20260803.md @@ -3,7 +3,7 @@ > 项目:qing/micro_servers(DIY 轻量微服务框架) > 场景:单机部署(所有服务同一台,仅 waf:443 对外) > 目标:在"单机 + 绑 127.0.0.1"基础上,补齐服务间认证与加密 -> 状态:计划 v1.0 +> 状态:计划 v1.0(阶段一部分完成) --- @@ -55,7 +55,7 @@ ### 阶段一:隔离加固(P0,先行) 对齐"绑 127.0.0.1 + 只放行 443"的基线。 -- [ ] 1.1 检查并修改所有服务监听地址为 `127.0.0.1`(禁止 `0.0.0.0`) +- [x] 1.1 检查并修改所有服务监听地址为 `127.0.0.1`(禁止 `0.0.0.0`)→ ✅ 已实现(db/confs/captcha/gateway/static/smtp/dnfs 均已绑定 127.0.0.1) - waf 例外:443 对外;但可考虑 waf 内部转发目标为 127.0.0.1:8443 - [ ] 1.2 云安全组 / ufw:仅放行 TCP 443,其余端口公网关闭 - 规则示例:`ufw default deny incoming` + `ufw allow 443/tcp` @@ -126,7 +126,7 @@ | 里程碑 | 内容 | 预估 | |--------|------|------| -| M1 | 阶段一:隔离加固完成,对外仅 443 | 0.5 天 | +| M1 | 阶段一:隔离加固完成,对外仅 443 | 0.5 天(127.0.0.1 已完成,剩余防火墙配置与验证) | | M2 | 阶段二:core 密钥注入 + 子进程读取 | 1 天 | | M3 | 阶段三:AES-GCM 库 + 接入全部服务 | 2-3 天 | | M4 | 回归测试 + 安全验证(篡改/伪造/重放) | 1 天 | diff --git a/docs/security/aibox_SECURITY_AUDIT_20260801.md b/docs/security/aibox_SECURITY_AUDIT_20260801.md index ff83ffc..3ad03cc 100644 --- a/docs/security/aibox_SECURITY_AUDIT_20260801.md +++ b/docs/security/aibox_SECURITY_AUDIT_20260801.md @@ -9,7 +9,7 @@ ## 代码验证记录 **验证日期**: 2026-08-03 -**验证方式**: 逐条对照审计报告与当前代码(`aibox.py` 358KB,关键区域 L3600-3850, L6540-6740, L7071-7160, L8410-8470) +**验证方式**: 逐条对照审计报告与当前代码(`aibox.py` 358KB,关键区域 L3600-3850, L6300-6400, L7071-7160, L8600-8660) ### 验证结果汇总 @@ -27,14 +27,14 @@ | 编号 | 状态 | 代码位置 | 验证说明 | |------|------|----------|----------| -| P0-1 | ❌ | L3664, L3751, L6553, L6715 | `shell=True` 仍在 4 处使用,命令注入未修复 | -| P0-2 | ❌ | L1177, L8423-8426 | 默认 `localhost:8765`(非 `127.0.0.1`),`handle_connection` 无任何认证 | +| P0-1 | ❌ | L3701, L3788, L6367 | `shell=True` 仍在 3 处使用,命令注入未修复 | +| P0-2 | ❌ | L1204, L8609 | 默认 `localhost:8765`(非 `127.0.0.1`),`handle_connection` 无任何认证 | | P1-1 | ❌ | L3943-3957, L5999-6017 | `check_path_safety` 仅检查文件大小,无 `realpath` 前缀校验 | | P1-2 | ❌ | L2037, L7365 | `requests.post` 无内网 IP 过滤 | | P1-3 | ❌ | 系统提示词 | 工具完整参数 schema 仍暴露 | | P1-4 | ❌ | L8326 | `api_key=None` 默认值,启动时未校验 | | P2-1 | ❌ | L7823 | `message_count` 仅统计未限流 | -| P2-2 | ⚠️ | L1582, L3580 | `TOOL_MAX_RESULT_LENGTH=100000`,`MAX_OUTPUT_BYTES=1MB`(有缓解) | +| P2-2 | ⚠️ | L1609, L3617 | `TOOL_MAX_RESULT_LENGTH=100000`,`MAX_OUTPUT_BYTES=1MB`(有缓解) | | P2-3 | ❌ | SQLite | 对话仍明文存储 | | P2-4 | ❌ | - | 临时文件清理未验证 | | P2-5 | ❌ | L4191-4251 | 技能 `.sh`/`.py` 直接加载执行 | @@ -42,7 +42,7 @@ | P3-1 | ❌ | - | 递归深度仍为 `max_recursion_depth=10` | | P3-2 | ❌ | - | 对话压缩可能丢失工具调用上下文 | | P3-3 | ❌ | - | 多连接竞争条件未加固 | -| P3-4 | ⚠️ | L1177 | 默认 `localhost`(非 `0.0.0.0` 也非 `127.0.0.1`) | +| P3-4 | ⚠️ | L1204 | 默认 `localhost`(非 `0.0.0.0` 也非 `127.0.0.1`) | | P3-5 | ❌ | - | 优雅关闭未验证 | --- @@ -51,10 +51,10 @@ ### P0-1: 命令注入漏洞 ❌ -- **位置**: `aibox.py` L3664, L3751, L6553, L6715 +- **位置**: `aibox.py` L3701, L3788, L6367 - **风险描述**: ```python - # 当前危险实现(4处) + # 当前危险实现(3处) subprocess.Popen(command, shell=True, ...) subprocess.run(command, shell=True, capture_output=True, text=True) ``` @@ -72,7 +72,7 @@ ### P0-2: WebSocket 无认证机制 ❌ -- **位置**: `aibox.py` L8423-8426, L1177 +- **位置**: `aibox.py` L8609, L1204 - **风险描述**: - WebSocket 默认监听 `localhost:8765`(已从 `0.0.0.0` 收敛,但仍非 `127.0.0.1`) - `handle_connection` 无任何 token、密码或 session 认证 @@ -170,12 +170,12 @@ ## ✅ 已有安全机制 -1. ✅ **工具结果清洗**: `_sanitize_tool_result` 过滤控制字符(L7071-7087) -2. ✅ **OOM 防护**: `MAX_OUTPUT_BYTES = 1MB`,`_read_with_limit` 截断输出(L3580-3630) -3. ✅ **工具结果大小限制**: `TOOL_MAX_RESULT_LENGTH=100000` 截断(L1582, L7153-7155) -4. ✅ **三层中断机制**: `_is_cancelled` + `create_task` 异步解耦 + 连接关闭取消 Task(L7106, L8431, L8445) -5. ✅ **fail-closed 设计**: 工具异常时安全返回错误信息,不暴露内部状态(L7127-7130) -6. ✅ **对话自动压缩**: Token/消息数超限自动压缩,防止上下文无限增长(L1700-1740) +1. ✅ **工具结果清洗**: `_sanitize_tool_result` 过滤控制字符(L7080-7096) +2. ✅ **OOM 防护**: `MAX_OUTPUT_BYTES = 1MB`,`_read_with_limit` 截断输出(L3617-3670) +3. ✅ **工具结果大小限制**: `TOOL_MAX_RESULT_LENGTH=100000` 截断(L1609, L7111) +4. ✅ **三层中断机制**: `_is_cancelled` + `create_task` 异步解耦 + 连接关闭取消 Task(L6890, L8617, L8650) +5. ✅ **fail-closed 设计**: 工具异常时安全返回错误信息,不暴露内部状态(L7167 附近) +6. ✅ **对话自动压缩**: Token/消息数超限自动压缩,防止上下文无限增长(L1753-2202) 7. ✅ **消息回滚机制**: API 失败时 `_rollback_messages` 回滚对话,防止坏消息污染历史 8. ✅ **提示词软约束**: 系统提示词中明确要求 AI 不要执行危险命令(需配合代码层面硬约束) diff --git a/docs/security/captcha_SECURITY_AUDIT_20260801.md b/docs/security/captcha_SECURITY_AUDIT_20260801.md index 31aa286..f2b2928 100644 --- a/docs/security/captcha_SECURITY_AUDIT_20260801.md +++ b/docs/security/captcha_SECURITY_AUDIT_20260801.md @@ -78,7 +78,7 @@ ### NEW-1: 验证码答案使用 random 模块生成(P2) -- **位置**: `captcha_service.py` L134-135 +- **位置**: `captcha_service.py` L146 - **问题**: `generate_text()` 使用 `random.choices` 而非 `secrets` 模块生成验证码答案。`random` 模块是确定性伪随机,若攻击者能推测随机种子状态,可预测验证码答案 - **影响**: 低(Token 使用 `secrets.token_hex` 保护,攻击者需先获取有效 Token) - **建议**: 将 `random.choices` 替换为 `secrets.SystemRandom().choices` 或逐个调用 `secrets.choice` diff --git a/docs/security/core_SECURITY_AUDIT_20260801.md b/docs/security/core_SECURITY_AUDIT_20260801.md index 4214881..f39bc14 100644 --- a/docs/security/core_SECURITY_AUDIT_20260801.md +++ b/docs/security/core_SECURITY_AUDIT_20260801.md @@ -20,10 +20,10 @@ ## 🔴 P0 — 严重问题 ### P0-1: PMC HTTP 管理接口无认证,可执行任意系统命令 -- **位置**: `src/main/HttpTask.cpp` L53、L130-142 +- **位置**: `src/main/HttpTask.cpp` L58、L130-142 - **当前状态**: ⚠️ 部分修复 - - ✅ 管理接口已绑定 `127.0.0.1`(`HttpTask.cpp` L53),仅本地可访问 - - ✅ MQ 通道的 `kill`/`exec` 操作已注释禁用(`main.cpp` L282-296),注释标注"在身份验证之前禁止操作" + - ✅ 管理接口已绑定 `127.0.0.1`(`HttpTask.cpp` L58),仅本地可访问 + - ❌ MQ 通道的 `kill`/`exec` 操作**仍在活跃执行**(`main.cpp` L283-296),L282 注释标注"在身份验证之前禁止操作"但代码未实际禁用 - ❌ **HTTP 端点 `/exec`、`/kill`、`/list` 仍无任何认证**(`HttpTask.cpp` L130-142),本地任意进程可调用 - ❌ 无操作审计日志 - **修复建议**: @@ -73,8 +73,8 @@ 3. ✅ **日志集中管理**: 统一日志格式和输出 4. ✅ **C++ RAII 内存管理**: 使用智能指针减少内存泄漏风险 5. ✅ **依赖审计**: 使用经过审计的 libth 线程库、libhttp HTTP 库、libcrypt 加密库 -6. ✅ **管理接口本地绑定**: HTTP 管理接口仅监听 `127.0.0.1`(`HttpTask.cpp` L53) -7. ✅ **MQ 通道危险操作已禁用**: `kill`/`exec` 通过消息队列的路径已注释,等待认证机制就绪(`main.cpp` L282-296) +6. ✅ **管理接口本地绑定**: HTTP 管理接口仅监听 `127.0.0.1`(`HttpTask.cpp` L58) +7. ❌ **MQ 通道危险操作未禁用**: `kill`/`exec` 通过消息队列的路径**仍在活跃执行**(`main.cpp` L283-296),L282 注释标注"在身份验证之前禁止操作"但代码未实际注释禁用 --- @@ -82,6 +82,7 @@ ### 立即(24小时) - [x] 确认 PMC HTTP 接口仅绑定 127.0.0.1 → ✅ 已确认 +- [ ] **禁用或保护 MQ 通道 kill/exec**(`main.cpp` L283-296 当前仍活跃,注释标注禁止但未实际执行) - [ ] PMC HTTP 接口 `/exec`、`/kill` 添加认证(当前仅绑定 localhost 不足以防御本地恶意进程) - [ ] 检查 init.txt 文件权限 @@ -117,7 +118,7 @@ | 编号 | 状态 | 代码位置 | 验证说明 | |------|------|----------|----------| -| P0-1 | ⚠️ 部分 | `HttpTask.cpp` L53, L130-142 | 已绑定 127.0.0.1;MQ 通道 kill/exec 已注释;但 HTTP 端点 `/exec`/`/kill` 仍无认证 | +| P0-1 | ⚠️ 部分 | `HttpTask.cpp` L58, L130-142 | 已绑定 127.0.0.1;但 MQ 通道 kill/exec **未禁用**(`main.cpp` L283-296 仍活跃);HTTP 端点 `/exec`/`/kill` 仍无认证 | | P0-2 | ❌ 未修复 | `pmc_mtd.cpp` L90-118 | `parse_self_init_list` 无完整性校验、无大小限制、无命令内容校验 | | P1-1 | ❌ 未修复 | `PPool.cpp` | `crtp()` 无熔断/频率限制;`ProcessTask.cpp` 在 `#ifdef __NEW_VER` 下未启用 | | P1-2 | ❌ 未修复 | `pmc_mtd.cpp` L90-118 | init.txt 解析未校验子进程日志路径参数 | @@ -130,6 +131,6 @@ | 措施 | 代码位置 | 说明 | |------|----------|------| -| MQ 通道危险操作禁用 | `main.cpp` L282-296 | `kill`/`exec` 通过消息队列的路径已注释,注释标注"在身份验证之前禁止操作" | -| HTTP 管理接口本地绑定 | `HttpTask.cpp` L53 | 显式绑定 `127.0.0.1`,非 `0.0.0.0` | +| MQ 通道危险操作**未禁用** | `main.cpp` L282-296 | `kill`/`exec` 通过消息队列的路径**仍活跃**,L282 注释标注"在身份验证之前禁止操作"但未实际注释代码 | +| HTTP 管理接口本地绑定 | `HttpTask.cpp` L58 | 显式绑定 `127.0.0.1`,非 `0.0.0.0` | | init.txt 注释行支持 | `pmc_mtd.cpp` L105-107 | 支持 `#` 注释行跳过,可禁用特定启动项 | diff --git a/docs/security/gateway_SECURITY_AUDIT_20260801.md b/docs/security/gateway_SECURITY_AUDIT_20260801.md index 74dc945..69daa87 100644 --- a/docs/security/gateway_SECURITY_AUDIT_20260801.md +++ b/docs/security/gateway_SECURITY_AUDIT_20260801.md @@ -20,7 +20,7 @@ ## 🔴 P0 — 严重问题 ### P0-1: RSA 私钥存储在仓库中/硬编码 -- **位置**: `main.cpp` L681 `_KEYGEN = std::make_unique();` +- **位置**: `main.cpp` L686 `_KEYGEN = std::make_unique();` - **当前状态**: ⚠️ 部分修复 - 密钥由 `MyRSA::Generator` 在内存中运行时生成,**未硬编码在源码中**,**未随仓库分发** - 每个进程实例独立生成密钥对,满足"每个部署独立密钥"要求 @@ -37,12 +37,12 @@ | # | 问题 | 状态 | 说明 | |---|------|------|------| -| P1-1 | Token 吊销机制缺失 | ❌ 未修复 | `handleLogout` (L1184) 仅清除 Cookie,无服务端黑名单/吊销列表;修改密码/注销后旧 Token 仍有效直到过期 | -| P1-2 | 登录失败次数限制 | ⚠️ 部分修复 | IP 级限流已实现 (`_LOGIN_RATE` L371,`check_and_record(ip,5,300)` L1022),但**无账户级锁定**(同一邮箱多次失败不触发账户锁定) | -| P1-3 | 上游代理超时配置 | ❌ 未修复 | `handleProxy` (L1947) 中 `HttpClient` 未显式设置超时,依赖 libhttp 默认值 | +| P1-1 | Token 吊销机制缺失 | ❌ 未修复 | `handleLogout` (L1195) 仅清除 Cookie,无服务端黑名单/吊销列表;修改密码/注销后旧 Token 仍有效直到过期 | +| P1-2 | 登录失败次数限制 | ⚠️ 部分修复 | IP 级限流已实现 (`_LOGIN_RATE` L376,`check_and_record(ip,5,300)` L1033),但**无账户级锁定**(同一邮箱多次失败不触发账户锁定) | +| P1-3 | 上游代理超时配置 | ❌ 未修复 | `handleProxy` (L2084) 中 `HttpClient` 未显式设置超时,依赖 libhttp 默认值 | | P1-4 | C++ HTTP 解析器缓冲区溢出审计 | ❌ 未修复 | libhttp 库(`src/shared/`)需全面 fuzz 测试 | | P1-5 | 请求体大小限制 | ❌ 未修复 | 未配置显式请求体大小上限(如 10MB),依赖 Boost.Beast 默认行为 | -| P1-6 | 反向代理响应无大小限制 | ❌ 未修复 | `handleProxy` 回调 L2069-2074 拼接下游响应 body 无上限(`res.body() += ret`,带 `FIXME: 增加长度限制`),下游(static/dnfs)返回超大响应可导致 OOM(全局审计 P1-1) | +| P1-6 | 反向代理响应无大小限制 | ❌ 未修复 | `handleProxy` 回调 L2212-2214 拼接下游响应 body 无上限(`res.body() += ret`,带 `FIXME: 增加长度限制`),下游(static/dnfs)返回超大响应可导致 OOM(全局审计 P1-1) | --- @@ -50,9 +50,9 @@ | # | 问题 | 状态 | 说明 | |---|------|------|------| -| P2-1 | 错误页面泄露版本号/堆栈信息 | ✅ 已修复 | `wrapperHttp` (L909-940) 异常捕获返回统一 `"Internal Server Error"`;`ExceptionBase::build_message()` 中文件/行号/函数名已注释(`ExpBase.hpp` L173-197),`what()` 仅含业务消息,不向客户端暴露源码位置 | -| P2-2 | HTTPS TLS 版本和密码套件安全 | ❌ 未修复 | `main.cpp` L677 TLS 模式下仅指定证书文件,未显式限定 TLS 版本和 CipherSuites | -| P2-3 | CORS 通配(反射任意 Origin) | ❌ 未修复 | **根因**:`_CORS_ORIGINS` 白名单 (L593) 已读取但**从未用于响应头**——OPTIONS 预检由 libhttp 库处理(`HttpServer_Request.cpp` L73-74),直接反射请求 Origin,libhttp 不感知 gateway 的 `_CORS_ORIGINS` 状态,等于允许任意来源(全局审计 P2)。修复需在 libhttp 层接入白名单或由 gateway 注册显式 OPTIONS 路由 | +| P2-1 | 错误页面泄露版本号/堆栈信息 | ✅ 已修复 | `wrapperHttp` (L920-965) 异常捕获返回统一 `"Internal Server Error"`;`ExceptionBase::build_message()` 中文件/行号/函数名已注释(`ExpBase.hpp` L173-197),`what()` 仅含业务消息,不向客户端暴露源码位置 | +| P2-2 | HTTPS TLS 版本和密码套件安全 | ❌ 未修复 | `main.cpp` L682 TLS 模式下仅指定证书文件,未显式限定 TLS 版本和 CipherSuites | +| P2-3 | CORS 通配(反射任意 Origin) | ❌ 未修复 | **根因**:`_CORS_ORIGINS` 白名单 (L598) 已读取但**从未用于响应头**——OPTIONS 预检由 libhttp 库处理(`HttpServer_Request.cpp` L73-74),直接反射请求 Origin,libhttp 不感知 gateway 的 `_CORS_ORIGINS` 状态,等于允许任意来源(全局审计 P2)。修复需在 libhttp 层接入白名单或由 gateway 注册显式 OPTIONS 路由 | | P2-4 | HTTP 服务器无超时配置 | ❌ 未修复 | 未设置 Read/Write/Idle 超时,`HttpServer(addr,port,threads)` 构造函数无超时参数,依赖 libhttp 默认行为,存在 Slowloris/连接耗尽风险(全局审计 P2) | --- @@ -63,15 +63,15 @@ 2. ✅ **RSA Token 签名**: Token 使用 RSA 私钥签名,防伪造 (`main.cpp` L1112-1130) 3. ✅ **反向代理架构**: 统一入口,隐藏后端服务拓扑 4. ✅ **速率限制**: 多层防护(IP 级 + 邮箱级双窗口限流、60秒冷却、失败锁定) -5. ✅ **防用户名枚举**: 用户不存在时执行 dummy PBKDF2 校验拉平响应时序 (`main.cpp` L1102-1108) +5. ✅ **防用户名枚举**: 用户不存在时执行 dummy PBKDF2 校验拉平响应时序 (`main.cpp` L1113-1118) 6. ✅ **WAF 前置防护**: Go WAF 处理 SQLi/XSS/CC/协议解析 7. ✅ **C++ 高性能实现**: 避免 Python GIL 瓶颈,抗并发能力强 8. ✅ **依赖审计**: 使用项目自带的 libcrypt 加密库(PBKDF2、RSA) -9. ✅ **路径遍历防护**: `is_safe_target()` 校验请求路径,拒绝 `..`、空字节、URL 编码遍历 (`main.cpp` L2161-2186) -10. ✅ **X-Forwarded-For 可信注入**: 剥离客户端伪造的 IP 头后由 gateway 主动设置 (`main.cpp` L2088-2098, `is_special_header` L2126-2138) -11. ✅ **优雅关闭**: SIGINT/SIGTERM 信号处理 (`main.cpp` L484-490) -12. ✅ **Token 刷新机制**: 支持宽限期刷新 + 次数上限 + 限流 (`main.cpp` L1339-1450) -13. ✅ **CAPTCHA 强制校验**: 登录/注册/发送验证码均需 CAPTCHA,对接 captcha 服务 (`main.cpp` L438, 调用点 L1044/1671/1784/1905) +9. ✅ **路径遍历防护**: `is_safe_target()` 校验请求路径,拒绝 `..`、空字节、URL 编码遍历 (`main.cpp` L2301-2326) +10. ✅ **X-Forwarded-For 可信注入**: 剥离客户端伪造的 IP 头后由 gateway 主动设置 (`main.cpp` L2229, `is_special_header` L2266-2280) +11. ✅ **优雅关闭**: SIGINT/SIGTERM 信号处理 (`main.cpp` L482-496) +12. ✅ **Token 刷新机制**: 支持宽限期刷新 + 次数上限 + 限流 (`main.cpp` L1463-1590) +13. ✅ **CAPTCHA 强制校验**: 登录/注册/发送验证码均需 CAPTCHA,对接 captcha 服务 (`main.cpp` L443, 调用点 L1048-1055/1707-1713/1801-1810/1915-1923) 14. ✅ **异步日志**: 批量推送 + fail-safe,db 不可用时静默丢弃 (`LogC.hpp`) --- @@ -117,26 +117,26 @@ | 编号 | 状态 | 代码位置 | 验证说明 | |------|------|----------|----------| -| P0-1 | ⚠️ 部分 | `main.cpp` L681 | `MyRSA::Generator()` 运行时生成,非硬编码;但无持久化,重启后 Token 全部失效 | -| P1-1 | ❌ 未修复 | `main.cpp` L1184-1198 | `handleLogout` 仅清除 Cookie,无 Token 黑名单/吊销机制 | -| P1-2 | ⚠️ 部分 | `main.cpp` L371, L1022 | IP 限流已实现 (5次/300s),无账户级锁定 | -| P1-3 | ❌ 未修复 | `main.cpp` L1947-2124 | `handleProxy` 中 `HttpClient` 未显式设置超时 | +| P0-1 | ⚠️ 部分 | `main.cpp` L686 | `MyRSA::Generator()` 运行时生成,非硬编码;但无持久化,重启后 Token 全部失效 | +| P1-1 | ❌ 未修复 | `main.cpp` L1195-1209 | `handleLogout` 仅清除 Cookie,无 Token 黑名单/吊销机制 | +| P1-2 | ⚠️ 部分 | `main.cpp` L376, L1033 | IP 限流已实现 (5次/300s),无账户级锁定 | +| P1-3 | ❌ 未修复 | `main.cpp` L2084-2262 | `handleProxy` 中 `HttpClient` 未显式设置超时 | | P1-4 | ❌ 未修复 | `src/shared/` | libhttp 库未进行 fuzz 测试 | | P1-5 | ❌ 未修复 | `main.cpp` | 未设置请求体大小上限 | -| P1-6 | ❌ 未修复 | `main.cpp` L2069-2074 | `handleProxy` 回调 `res.body() += ret` 拼接下游响应 body 无上限,超大响应可 OOM | -| P2-1 | ✅ 已修复 | `main.cpp` L909-940, `ExpBase.hpp` L173-197 | 异常统一返回 `"Internal Server Error"`;`build_message()` 已注释文件/行号/函数名,不泄露源码位置 | -| P2-2 | ❌ 未修复 | `main.cpp` L677 | TLS 模式仅指定证书,未限定版本和密码套件 | -| P2-3 | ❌ 未修复 | `main.cpp` L593, libhttp `HttpServer_Request.cpp` L73-74 | `_CORS_ORIGINS` 白名单读取但未用于响应头(OPTIONS 由 libhttp 处理,不感知 gateway 状态);OPTIONS 预检反射任意 Origin | +| P1-6 | ❌ 未修复 | `main.cpp` L2212-2214 | `handleProxy` 回调 `res.body() += ret` 拼接下游响应 body 无上限,超大响应可 OOM | +| P2-1 | ✅ 已修复 | `main.cpp` L920-965, `ExpBase.hpp` L173-197 | 异常统一返回 `"Internal Server Error"`;`build_message()` 已注释文件/行号/函数名,不泄露源码位置 | +| P2-2 | ❌ 未修复 | `main.cpp` L682 | TLS 模式仅指定证书,未限定版本和密码套件 | +| P2-3 | ❌ 未修复 | `main.cpp` L598, libhttp `HttpServer_Request.cpp` L73-74 | `_CORS_ORIGINS` 白名单读取但未用于响应头(OPTIONS 由 libhttp 处理,不感知 gateway 状态);OPTIONS 预检反射任意 Origin | | P2-4 | ❌ 未修复 | `main.cpp` L677-679, libhttp `HttpServer` | `HttpServer(addr,port,threads)` 无超时参数,未设置 HTTP 服务器 Read/Write/Idle 超时 | ### 新增安全机制(审计报告未覆盖) | 机制 | 代码位置 | 说明 | |------|----------|------| -| 路径遍历防护 | `main.cpp` L2161-2186 | `is_safe_target()` 拒绝 `..`、空字节、URL 编码遍历 | -| XFF 可信注入 | `main.cpp` L2088-2098, L2126-2138 | `is_special_header` 剥离客户端伪造 IP 头后主动设置可信 XFF | -| 优雅关闭 | `main.cpp` L484-490 | SIGINT/SIGTERM 信号处理 | -| 防用户枚举 | `main.cpp` L1102-1108 | 用户不存在时执行 dummy PBKDF2 拉平响应时序 | -| 密码强度校验 | `main.cpp` L146-176 | 8-128字符,至少3种字符类别 | -| 邮箱双窗口限流 | `main.cpp` L1677-1681 | 3次/小时 + 10次/天/邮箱 | +| 路径遍历防护 | `main.cpp` L2301-2326 | `is_safe_target()` 拒绝 `..`、空字节、URL 编码遍历 | +| XFF 可信注入 | `main.cpp` L2229, L2266-2280 | `is_special_header` 剥离客户端伪造 IP 头后主动设置可信 XFF | +| 优雅关闭 | `main.cpp` L482-496 | SIGINT/SIGTERM 信号处理 | +| 防用户枚举 | `main.cpp` L1113-1118 | 用户不存在时执行 dummy PBKDF2 拉平响应时序 | +| 密码强度校验 | `main.cpp` L146-180 | 8-128字符,至少3种字符类别 | +| 邮箱双窗口限流 | `main.cpp` L1814-1818 | 3次/小时 + 10次/天/邮箱 | | 异步日志 fail-safe | `LogC.hpp` L210-213 | db 不可用时静默丢弃,绝不影响业务 | diff --git a/docs/security/libhttp_SECURITY_AUDIT_20260803.md b/docs/security/libhttp_SECURITY_AUDIT_20260803.md index 61d86d1..4a83db9 100644 --- a/docs/security/libhttp_SECURITY_AUDIT_20260803.md +++ b/docs/security/libhttp_SECURITY_AUDIT_20260803.md @@ -25,7 +25,7 @@ | 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-5 | 静态文件目录路径遍历 | ✅ 已修复 | 新增 `isSafeStaticPath()`(`HttpServer_Request.cpp` L140-157),静态文件服务前校验 target_path(拒绝 `..`、空字节、非 `/` 开头、URL 编码 `%2e`/`%2f`/`%5c`),逻辑与 gateway `is_safe_target()` 对齐(`HttpServer.cpp` L94) | | P1-6 | 发送队列无界(慢消费者 OOM) | ✅ 已修复 | `HttpServer` 新增 `setMaxWsQueueSize()`(默认 1000),`wsSend` / `onProxyReadClient` / `onProxyReadBackend` 入队前检查,超过上限判定慢消费者/慢后端,关闭连接并清空队列,防止内存耗尽 | --- @@ -73,7 +73,7 @@ ### 短期(72小时) - [x] P1-3 WS 消息大小限制:设置 `read_message_max`(可配置)→ ✅ 已实现(`setMaxWsMessageSize()`,默认 16MB,服务端与代理流均生效) -- [x] P1-5 静态文件路径遍历:新增 `isSafeStaticPath()` 拼接前校验 `target_path`,拒绝 `..`/空字节/编码遍历 → ✅ 已实现(`HttpServer_Request.cpp` L139-157) +- [x] P1-5 静态文件路径遍历:新增 `isSafeStaticPath()` 拼接前校验 `target_path`,拒绝 `..`/空字节/编码遍历 → ✅ 已实现(`HttpServer_Request.cpp` L140-157) - [x] P1-6 发送队列上限:`ws_send_queue_`/代理队列设最大值,超限断开慢消费者 → ✅ 已实现(`setMaxWsQueueSize()`,默认 1000,入队前检查超限即关闭) - [ ] P2-1 CORS 白名单:origin 白名单校验,替代反射任意 Origin @@ -97,6 +97,7 @@ | setMaxBodySize/getMaxBodySize | `inc/us/HttpServer.hpp` | L118-120 | | 请求处理/静态文件 | `src/HttpServer_Request.cpp` | L7-137, L94-119 | | CORS 反射 Origin | `src/HttpServer_Request.cpp` | L73-74 | +| isSafeStaticPath 路径校验 | `src/HttpServer_Request.cpp` | L140-157 | | 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 | @@ -106,4 +107,4 @@ | 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` | 全篇 | \ No newline at end of file +| ~~遗留 WS 实现(已删除)~~ | ~~`src/WebSocketServer.cpp`、`src/WebSocketClient.cpp`~~ | 已删除(见 P3-1),WebSocket 能力由 `HttpServer` + `HttpClient` 提供 | \ No newline at end of file