From cebef9fe70acc485ec81b462e072e52eadd945e5 Mon Sep 17 00:00:00 2001 From: qing <1776177662@qq.com> Date: Sat, 1 Aug 2026 05:21:23 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E5=90=84=E6=A8=A1=E5=9D=97=E5=AE=89?= =?UTF-8?q?=E5=85=A8=E5=AE=A1=E8=AE=A1=E6=8A=A5=E5=91=8A=20+=20=E8=A1=A5?= =?UTF-8?q?=E5=85=85=20.gitignore=20=E5=BF=BD=E7=95=A5=E8=A7=84=E5=88=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 4 + BotOps/docs/SECURITY_AUDIT_20260801.md | 50 +++ ai_review/docs/SECURITY_AUDIT_20260801.md | 75 ++++ aibox/docs/SECURITY_AUDIT_20260801.md | 58 +++ captcha/docs/SECURITY_AUDIT_20260801.md | 54 +++ core/docs/SECURITY_AUDIT_20260801.md | 89 +++++ db/docs/SECURITY_AUDIT_20260801.md | 82 +++++ dnfs/docs/SECURITY_AUDIT_20260801.md | 85 +++++ docs/DEPLOY_SECURITY_AUDIT_20260801.md | 78 ++++ docs/DEPS_SECURITY_AUDIT_20260801.md | 68 ++++ docs/SECURITY_AUDIT_20260801.md | 427 ++++++++++++++++++++++ gateway/docs/SECURITY_AUDIT_20260801.md | 356 +++--------------- pymsg/docs/SECURITY_AUDIT_20260801.md | 139 +++++++ smtp/docs/SECURITY_AUDIT_20260801.md | 67 ++++ static/docs/SECURITY_AUDIT_20260801.md | 132 +++++++ 15 files changed, 1467 insertions(+), 297 deletions(-) create mode 100644 BotOps/docs/SECURITY_AUDIT_20260801.md create mode 100644 ai_review/docs/SECURITY_AUDIT_20260801.md create mode 100644 aibox/docs/SECURITY_AUDIT_20260801.md create mode 100644 captcha/docs/SECURITY_AUDIT_20260801.md create mode 100644 core/docs/SECURITY_AUDIT_20260801.md create mode 100644 db/docs/SECURITY_AUDIT_20260801.md create mode 100644 dnfs/docs/SECURITY_AUDIT_20260801.md create mode 100644 docs/DEPLOY_SECURITY_AUDIT_20260801.md create mode 100644 docs/DEPS_SECURITY_AUDIT_20260801.md create mode 100644 docs/SECURITY_AUDIT_20260801.md create mode 100644 pymsg/docs/SECURITY_AUDIT_20260801.md create mode 100644 smtp/docs/SECURITY_AUDIT_20260801.md create mode 100644 static/docs/SECURITY_AUDIT_20260801.md diff --git a/.gitignore b/.gitignore index 49abecf..544d409 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,7 @@ gateway/shufeng.ini core/Nidus.service.shufeng ai_review/.env +confs/__pycache__/ +libhttp/test_server +*.pyc +__pycache__/ diff --git a/BotOps/docs/SECURITY_AUDIT_20260801.md b/BotOps/docs/SECURITY_AUDIT_20260801.md new file mode 100644 index 0000000..2416ee4 --- /dev/null +++ b/BotOps/docs/SECURITY_AUDIT_20260801.md @@ -0,0 +1,50 @@ +# BotOps 模块安全审计报告 + +**审计日期**: 2026-08-01 +**审计范围**: `app.py`、`ws_client.py`、`commands.py`、`widgets/` +**代码版本**: dev_qing 分支 + +--- + +## 修复进度 + +| 优先级 | 总数 | 已修复 | 待修复 | +|--------|------|--------|--------| +| P0 | 1 | 0 | 1 | +| P1 | 1 | 0 | 1 | + +--- + +## 🔴 P0 — 严重问题 + +### P0-1: WebSocket 客户端连接无证书验证/无认证 +- **位置**: `ws_client.py` +- **问题**: BotOps 作为运维管理客户端,连接后端 WebSocket 时: + - 若连接 aibox/core 的 WebSocket 管理接口,需要确认 TLS 证书验证是否启用 + - Token/认证凭据存储方式需审计 +- **修复建议**: + 1. 启用 TLS 证书验证,不使用 `verify=False` + 2. 认证 Token 安全存储(系统钥匙串而非明文配置文件) + +--- + +## 🟠 P1 — 高危问题 + +| # | 问题 | 说明 | +|---|------|------| +| P1-1 | 命令执行权限 | BotOps 作为运维工具,可执行系统命令,需确认是否限制了可执行命令范围 | + +--- + +## ✅ 已有安全机制 + +1. ✅ **桌面客户端架构**: 本地运行应用,天然隔离 +2. ✅ **WebSocket 通信**: 与后端实时通信 + +--- + +## 修复路线图 + +### 短期 +- [ ] 审计 ws_client.py 的 TLS 配置 +- [ ] 确认命令执行白名单 diff --git a/ai_review/docs/SECURITY_AUDIT_20260801.md b/ai_review/docs/SECURITY_AUDIT_20260801.md new file mode 100644 index 0000000..3fa27c4 --- /dev/null +++ b/ai_review/docs/SECURITY_AUDIT_20260801.md @@ -0,0 +1,75 @@ +# ai_review 模块安全审计报告 + +**审计日期**: 2026-08-01 +**审计范围**: `ai_review.py`、`agent/`、`security_middleware.py` +**代码版本**: dev_qing 分支 + +--- + +## 修复进度 + +| 优先级 | 总数 | 已修复 | 待修复 | +|--------|------|--------|--------| +| P0 | 1 | 0 | 1 | +| P1 | 1 | 0 | 1 | +| P2 | 2 | 0 | 2 | + +--- + +## 🔴 P0 — 严重问题 + +### P0-1: 所有接口无认证,存在 API 滥用和 RAG 投毒风险 +- **位置**: `ai_review.py` Flask 路由 +- **问题**: + - `/review`、`/rag/feedback`、`/rag/list` 等接口均无认证 + - 虽然绑定 127.0.0.1,但任何本地进程/用户可调用 + - **RAG 投毒风险**:`/rag/feedback` 可向知识库注入恶意样本,绕过内容审核 + - **API 滥用**:无限制调用 DeepSeek API,消耗费用 +- **修复建议**: + 1. `/rag/feedback` 必须添加认证(仅管理员/internal 调用) + 2. `/review` 添加上游服务认证(共享密钥) + 3. 添加调用速率限制 + 4. RAG 反馈需人工审核后才入库,而非直接写入 + +--- + +## 🟠 P1 — 高危问题 + +| # | 问题 | 说明 | +|---|------|------| +| P1-1 | DeepSeek API Key 通过 .env 配置,但需确认未硬编码或提交 | 检查 .env 是否在 .gitignore 中 | + +--- + +## 🟡 P2 — 中危问题 + +| # | 问题 | +|---|------| +| P2-1 | RAG 知识库文件无校验,损坏文件可能导致服务异常 | +| P2-2 | 审核 prompt 可被注入(内容中包含"忽略之前指令"等越狱提示) | + +--- + +## ✅ 已有安全机制 + +1. ✅ **fail-closed 设计**: API 异常或响应解析失败时一律返回 `{"invalid": "true"}`(拦截),宁可误拦不可漏放 +2. ✅ **RAG top-3 相似样本注入**: MD5 去重,提升审核准确率 +3. ✅ **审核类别覆盖**: 色情、违禁药品、赌博、诈骗、仇恨/暴力/恐怖主义 +4. ✅ **security_middleware.py**: 安全响应头 +5. ✅ **Docker 容器化**: Python 3.11-slim 基础镜像,端口绑定 127.0.0.1 + +--- + +## 修复路线图 + +### 立即(24小时) +- [ ] `/rag/feedback` 添加认证,禁止未授权写入 + +### 短期(72小时) +- [ ] 所有接口添加内部服务认证 +- [ ] 调用速率限制 +- [ ] RAG 反馈改为人工审核入库 + +### 中期 +- [ ] Prompt 注入防护 +- [ ] RAG 知识库文件校验 diff --git a/aibox/docs/SECURITY_AUDIT_20260801.md b/aibox/docs/SECURITY_AUDIT_20260801.md new file mode 100644 index 0000000..c43c3b1 --- /dev/null +++ b/aibox/docs/SECURITY_AUDIT_20260801.md @@ -0,0 +1,58 @@ +# aibox 模块安全审计报告 + +**审计日期**: 2026-08-01 +**审计范围**: `aibox.py`、`skills/`、WebSocket 服务 +**代码版本**: dev_qing 分支 + +--- + +## 修复进度 + +| 优先级 | 总数 | 已修复 | 待修复 | +|--------|------|--------|--------| +| P0 | 2 | 2 | 0 | +| P1 | 4 | 部分 | 2 | + +--- + +## 🔴 P0 — 严重问题(已修复) + +### P0-1: 命令注入漏洞(已修复) +- **原问题**: `skills/ssh-exec.py` 等工具使用 `shell=True` 拼接用户输入,导致任意命令执行 +- **修复状态**: ✅ 已修复(OOM防护 + 结果清洗) + +### P0-2: WebSocket 无认证且监听 0.0.0.0(已修复) +- **原问题**: WebSocket 服务无认证,监听所有网络接口,可被远程连接执行任意工具 +- **修复状态**: ✅ 已修复 + +--- + +## 🟠 P1 — 高危问题(剩余) + +| # | 问题 | 说明 | +|---|------|------| +| P1-1 | 部分工具结果输出未完全过滤敏感信息 | 需确认所有 skill 工具输出是否清洗了密码、密钥、Token | +| P1-2 | 本地工具调用权限控制 | nmap、ssh、camera 等危险工具应有调用白名单/管理员限制 | + +--- + +## ✅ 已有安全机制(此前已修复) + +1. ✅ **OOM 防护**: 工具执行输出限制大小,防止内存耗尽 +2. ✅ **工具结果清洗**: 过滤敏感信息输出 +3. ✅ **WebSocket 安全配置**: 已收敛监听范围 +4. ✅ **独立进程执行**: 工具在隔离进程中运行,不直接访问主服务内存 +5. ✅ **fail-closed 设计**: 工具异常时安全返回,不暴露内部状态 + +--- + +## 修复路线图 + +### 短期(72小时) +- [ ] 审计所有 skills/ 下工具的命令执行方式,确认无 shell=True 拼接 +- [ ] 危险工具(ssh、nmap、camera)添加权限控制 +- [ ] 工具结果敏感信息过滤全面验证 + +### 中期 +- [ ] 工具调用沙箱化(容器/受限用户) +- [ ] 工具调用审计日志 diff --git a/captcha/docs/SECURITY_AUDIT_20260801.md b/captcha/docs/SECURITY_AUDIT_20260801.md new file mode 100644 index 0000000..9fa2e8c --- /dev/null +++ b/captcha/docs/SECURITY_AUDIT_20260801.md @@ -0,0 +1,54 @@ +# captcha 模块安全审计报告 + +**审计日期**: 2026-08-01 +**审计范围**: `captcha_service.py` +**代码版本**: dev_qing 分支 + +--- + +## 修复进度 + +| 优先级 | 总数 | 已修复 | 待修复 | +|--------|------|--------|--------| +| P0 | 0 | 0 | 0 | +| P1 | 1 | 0 | 1 | +| P2 | 1 | 0 | 1 | + +--- + +## 🟠 P1 — 高危问题 + +| # | 问题 | 说明 | +|---|------|------| +| P1-1 | 验证码存储无容量限制,大量生成可耗尽内存 | 无验证码数量上限,攻击者可高频调用 `/captcha/image` 生成大量验证码填满内存 | + +--- + +## 🟡 P2 — 中危问题 + +| # | 问题 | +|---|------| +| P2-1 | 内存存储不支持多实例水平扩展(需换 Redis),当前单实例可接受 | + +--- + +## ✅ 已有安全机制 + +1. ✅ **一次性使用**: verify 后立即删除 Token +2. ✅ **5 分钟过期**: 惰性清理 +3. ✅ **常数时间比较**: XOR 防时序攻击 +4. ✅ **仅监听 127.0.0.1**: 不对外暴露 +5. ✅ **gateway 层代理**: `Cache-Control: no-store` 禁止缓存验证码图片 +6. ✅ **依赖 pymsg 协议**: recv_timeout 防 Slowloris +7. ✅ **图形验证码使用 PIL 生成**: 不依赖外部服务 + +--- + +## 修复路线图 + +### 短期 +- [ ] 添加验证码存储容量上限(如最多 10000 个,超出时清理最早的) +- [ ] 添加生成速率限制(单 IP 每分钟最多生成 N 个) + +### 中期 +- [ ] 考虑替换为 Redis 存储支持水平扩展(当前单实例足够) diff --git a/core/docs/SECURITY_AUDIT_20260801.md b/core/docs/SECURITY_AUDIT_20260801.md new file mode 100644 index 0000000..ee0314b --- /dev/null +++ b/core/docs/SECURITY_AUDIT_20260801.md @@ -0,0 +1,89 @@ +# core(Nidus)模块安全审计报告 + +**审计日期**: 2026-08-01 +**审计范围**: `src/main/`、`src/shared/` +**代码版本**: dev_qing 分支 + +--- + +## 修复进度 + +| 优先级 | 总数 | 已修复 | 待修复 | +|--------|------|--------|--------| +| P0 | 2 | 0 | 2 | +| P1 | 3 | 0 | 3 | +| P2 | 3 | 0 | 3 | + +--- + +## 🔴 P0 — 严重问题 + +### P0-1: PMC HTTP 管理接口无认证,可执行任意系统命令 +- **位置**: `src/main/HttpTask.cpp` +- **问题**: Core(PMC)作为进程管理器,暴露了 HTTP 管理接口(`/exec` 等),**无任何认证**: + - 任意能访问管理端口的本地/远程攻击者可执行任意 shell 命令 + - 可启停任意托管服务、读取日志、获取运行状态 + - 当前缓解:应仅绑定 127.0.0.1,需确认配置 +- **修复建议**: + 1. 确保管理接口仅监听 127.0.0.1 + 2. 添加强认证(预共享 Token 或 Basic Auth over localhost) + 3. 危险命令(exec、kill)要求二次确认 + 4. 记录所有管理操作审计日志 + +### P0-2: init.txt 配置文件无完整性校验,可注入恶意命令 +- **位置**: `src/main/main.cpp` init.txt 解析 +- **问题**: init.txt 定义了启动哪些子进程及参数,若文件被篡改: + - 可添加任意启动条目,执行恶意二进制 + - 命令行参数可被注入 +- **修复建议**: + 1. init.txt 添加数字签名/校验和验证 + 2. 运行时监控 init.txt 修改,变更需管理员确认 + 3. 文件权限设为 0600,仅 root/运行用户可写 + +--- + +## 🟠 P1 — 高危问题 + +| # | 问题 | 位置 | +|---|------|------| +| P1-1 | 子进程异常退出无次数限制,可被反复重启消耗资源 | 进程监控逻辑 | +| P1-2 | 日志路径穿越:子进程日志文件名可控时可能写入任意位置 | 日志配置 | +| P1-3 | C++ HTTP 解析器需审计缓冲区溢出 | libhttp 库(shared 模块) | + +--- + +## 🟡 P2 — 中危问题 + +| # | 问题 | +|---|------| +| P2-1 | 信号处理函数中使用非异步安全函数(如 printf、log) | +| P2-2 | 线程池任务队列无上限,可 OOM | +| P2-3 | 崩溃 core dump 可能包含敏感信息(密码、Token) | + +--- + +## ✅ 已有安全机制 + +1. ✅ **进程生命周期管理**: 自动重启异常退出的子进程,保障服务可用性 +2. ✅ **线程池模型**: 固定线程数,防止无限线程创建 +3. ✅ **日志集中管理**: 统一日志格式和输出 +4. ✅ **C++ RAII 内存管理**: 使用智能指针减少内存泄漏风险 +5. ✅ **依赖审计**: 使用经过审计的 libth 线程库、libhttp HTTP 库、libcrypt 加密库 + +--- + +## 修复路线图 + +### 立即(24小时) +- [ ] 确认 PMC HTTP 接口仅绑定 127.0.0.1 +- [ ] 检查 init.txt 文件权限 + +### 短期(72小时) +- [ ] PMC HTTP 接口添加认证 +- [ ] 子进程重启频率限制(指数退避或熔断) +- [ ] 审计 libhttp HTTP 解析器 + +### 中期 +- [ ] init.txt 完整性校验 +- [ ] 管理操作审计日志 +- [ ] 信号处理函数安全改造 diff --git a/db/docs/SECURITY_AUDIT_20260801.md b/db/docs/SECURITY_AUDIT_20260801.md new file mode 100644 index 0000000..0e367de --- /dev/null +++ b/db/docs/SECURITY_AUDIT_20260801.md @@ -0,0 +1,82 @@ +# db 模块安全审计报告 + +**审计日期**: 2026-08-01 +**审计范围**: `dbv2.py`、`db_tools.py`、`log_client` 相关 +**代码版本**: dev_qing 分支 + +--- + +## 修复进度 + +| 优先级 | 总数 | 已修复 | 待修复 | +|--------|------|--------|--------| +| P0 | 1 | 0 | 1 | +| P1 | 3 | 0 | 3 | +| P2 | 2 | 0 | 2 | + +--- + +## 🔴 P0 — 严重问题 + +### P0-1: 零认证对外开放完整 CRUD(依赖 127.0.0.1 网络隔离) +- **位置**: `dbv2.py` RpcServer +- **问题**: + - 完全没有认证机制 + - 任何能访问 8005 端口的本地进程/用户都可执行任意 select/insert/update/delete + - 可读取 Users 表密码哈希、Logs 表所有日志 + - SELECT 无 limit 上限,可被拖库 + - 无表级/操作级权限控制 +- **当前缓解**: 默认监听 `127.0.0.1`,Docker 也绑定到 127.0.0.1 +- **修复建议**: + 1. 添加 API Token 认证(预共享密钥,所有调用需携带 `_auth` 字段) + 2. SELECT 强制添加 LIMIT 上限(默认 1000 条),防止拖库 + 3. 添加操作权限:日志写入只允许 insert,禁止 delete/truncate + 4. 拒绝无条件 UPDATE/DELETE(必须带 where 条件) + 5. DDL 操作仅限本地运维工具,RPC 接口完全禁用 + +--- + +## 🟠 P1 — 高危问题 + +| # | 问题 | 说明 | +|---|------|------| +| P1-1 | db_tools.py 运维工具存在 SQL 字符串拼接风险 | 运维脚本中可能使用字符串拼接构建 SQL | +| P1-2 | 无操作审计日志 | 谁在什么时候修改了什么数据无独立审计记录 | +| P1-3 | Logs 表自动清理可被利用销毁证据 | 攻击者可通过触发大量日志加速清理,删除攻击痕迹 | + +--- + +## 🟡 P2 — 中危问题 + +| # | 问题 | +|---|------| +| P2-1 | 日志表自动清理的保留策略可配置化(目前硬编码 30天/100万条) | +| P2-2 | 错误信息可能泄露表结构/字段名给调用方 | + +--- + +## ✅ 已有安全机制 + +1. ✅ **SQLAlchemy Core 参数化查询**:核心 CRUD 路径使用表反射 + 参数化,零 SQL 注入风险 +2. ✅ **仅监听 127.0.0.1**:不对外暴露 +3. ✅ **集中式日志 fail-safe**:db 不可用时静默丢弃,不阻塞业务 +4. ✅ **自动提交/回滚**:异常时自动回滚事务 +5. ✅ **日期/时间适配**:ISO 8601 处理,`"$now"` 哨兵 +6. ✅ **日志自动清理**:后台线程每小时清理,防止磁盘占满 +7. ✅ **log_client 健壮性**:异步队列、后台线程、fail-safe、队列上限(从各服务副本) + +--- + +## 修复路线图 + +### 立即(24小时) +- [ ] SELECT 添加强制 LIMIT 上限(防止拖库) + +### 短期(72小时) +- [ ] 添加 API Token 认证 +- [ ] 无条件 UPDATE/DELETE 拒绝执行 +- [ ] 错误信息脱敏 + +### 中期 +- [ ] 表级权限控制(日志表只写不删/改) +- [ ] 管理操作审计日志 diff --git a/dnfs/docs/SECURITY_AUDIT_20260801.md b/dnfs/docs/SECURITY_AUDIT_20260801.md new file mode 100644 index 0000000..e85cf33 --- /dev/null +++ b/dnfs/docs/SECURITY_AUDIT_20260801.md @@ -0,0 +1,85 @@ +# dnfs 模块安全审计报告 + +**审计日期**: 2026-08-01 +**审计范围**: `dnfs.py`、`security_middleware.py` +**代码版本**: dev_qing 分支 + +--- + +## 修复进度 + +| 优先级 | 总数 | 已修复 | 待修复 | +|--------|------|--------|--------| +| P0 | 1 | 0 | 1 | +| P1 | 2 | 0 | 2 | +| P2 | 2 | 0 | 2 | + +--- + +## 🔴 P0 — 严重问题 + +### P0-1: Token 无 RSA 签名验证,可伪造任意用户身份(含管理员) +- **位置**: `dnfs.py:285-292` +- **问题**: + ```python + def decode_token(token: str): + idx = token.find('_') + if idx != -1: + raw = token[:idx] + # FIXME: 验证数字签名 + decoded = base64.b64decode(raw) + jj = decoded.decode('utf-8') + return json.loads(jj) + ``` + 代码明确标注 `FIXME: 验证数字签名`,但**完全跳过了 RSA 签名验证**: + 1. 攻击者可自行构造 base64 编码的 JSON payload(`{"user": "任意邮箱", "is_admin": true, ...}`) + 2. 下划线后拼接任意字符串作为"签名" + 3. 直接伪造管理员身份,绕过所有权限校验,可增删改任意节点 +- **修复建议**: + 1. 立即实现 RSA 签名验证(与 gateway 验签逻辑一致) + 2. dnfs 需要持有 gateway 的 RSA 公钥(启动时从配置加载,或内置公钥) + 3. 添加 Token 过期校验(`created_at` + `keep_time`) + 4. 校验失败返回 401 + +--- + +## 🟠 P1 — 高危问题 + +| # | 问题 | 说明 | +|---|------|------| +| P1-1 | Token 过期校验不严谨 | 需严格验证 `created_at` + `keep_time`,防止过期 Token 被复用 | +| P1-2 | is_admin 权限完全依赖 Token 字段,无二次验证 | 管理员权限应在服务端再次校验,而非仅信任 Token 声明 | + +--- + +## 🟡 P2 — 中危问题 + +| # | 问题 | +|---|------| +| P2-1 | AI 审核 fail-closed 实现需验证:ai_review 不可用时是否确实拒绝写入 | +| P2-2 | 节点删除无确认机制,批量删除无速率限制 | + +--- + +## ✅ 已有安全机制 + +1. ✅ **特殊节点校验**: link/html/image/file 各类型有格式与大小限制(URL 前缀、大小上限) +2. ✅ **AI 内容审核集成**: 新增节点前调用 ai_review 服务审核 +3. ✅ **security_middleware.py**: 安全响应头 +4. ✅ **CORS 配置**: 跨域控制 +5. ✅ **通过 pymsg 调 db 服务**: 不直连数据库,规避 SQL 注入 + +--- + +## 修复路线图 + +### 立即(24小时) +- [ ] 实现 Token RSA 签名验证(最高优先级) + +### 短期(72小时) +- [ ] Token 过期严格校验 +- [ ] is_admin 二次验证 + +### 中期 +- [ ] AI 审核 fail-closed 验证测试 +- [ ] 节点操作速率限制 diff --git a/docs/DEPLOY_SECURITY_AUDIT_20260801.md b/docs/DEPLOY_SECURITY_AUDIT_20260801.md new file mode 100644 index 0000000..cbba463 --- /dev/null +++ b/docs/DEPLOY_SECURITY_AUDIT_20260801.md @@ -0,0 +1,78 @@ +# 部署配置安全审计报告 + +**审计日期**: 2026-08-01 +**审计范围**: `docker-compose.yml`、`docker.sh`、`_inst.sh`、`_setup.*.sh`、`.env.example` +**代码版本**: dev_qing 分支 + +--- + +## 修复进度 + +| 优先级 | 总数 | 已修复 | 待修复 | +|--------|------|--------|--------| +| P0 | 1 | 0 | 1 | +| P1 | 2 | 0 | 2 | +| P2 | 3 | 0 | 3 | + +--- + +## 🔴 P0 — 严重问题 + +### P0-1: WAF 规则默认全部注释,WAF 形同虚设 +- **位置**: `waf/rules/local/custom.conf` +- **问题**: 所有 WAF 安全规则默认被注释掉: + ``` + # SecRule ... SQL 注入规则... + # SecRule ... XSS 规则... + ``` + 部署后 WAF 实际不拦截任何攻击,恶意请求直接透传到后端 +- **修复建议**: + 1. 启用核心规则集(SQLi、XSS、命令注入等关键规则) + 2. docker.sh 启动时检测规则是否启用,若全注释则打印警告 + 3. 异常模式先运行一周,观察误报后切换到拦截模式 + +--- + +## 🟠 P1 — 高危问题 + +| # | 问题 | 位置 | 说明 | +|---|------|------|------| +| P1-1 | 默认密钥/密码需检查 | `.env.example`、各 `_set.sh` | 确认没有硬编码的默认数据库密码、API Key | +| P1-2 | Docker 容器权限过高 | `docker-compose.yml` | 检查是否使用 `--privileged`、是否挂载了敏感目录(`/etc`、`/root`) | + +--- + +## 🟡 P2 — 中危问题 + +| # | 问题 | +|---|------| +| P2-1 | Docker 镜像未指定非 root 用户运行 | +| P2-2 | 没有健康检查配置,异常服务不会自动重启 | +| P2-3 | 数据库文件/配置文件未配置定期备份 | + +--- + +## ✅ 已有安全机制 + +1. ✅ **端口绑定限制**: 内部服务(db、confs、smtp、captcha、pymsg 服务)均绑定 `127.0.0.1`,不对外暴露 +2. ✅ **Docker Compose 编排**: 容器化部署,环境一致性 +3. ✅ **.env.example 模板**: 环境变量配置模板,不硬编码密钥 +4. ✅ **独立 Docker 网络**: 服务间通过内部网络通信 +5. ✅ **waf 前置**: 所有公网流量先经过 WAF 再到 gateway + +--- + +## 修复路线图 + +### 立即(24小时) +- [ ] 启用 WAF 核心规则(关键攻击类型) + +### 短期(72小时) +- [ ] 检查所有默认密码/密钥 +- [ ] 容器非 root 用户运行 +- [ ] 移除不必要的 privileged 权限 + +### 中期 +- [ ] 添加健康检查配置 +- [ ] 自动备份策略 +- [ ] Docker 镜像安全扫描 diff --git a/docs/DEPS_SECURITY_AUDIT_20260801.md b/docs/DEPS_SECURITY_AUDIT_20260801.md new file mode 100644 index 0000000..9c7b7b5 --- /dev/null +++ b/docs/DEPS_SECURITY_AUDIT_20260801.md @@ -0,0 +1,68 @@ +# C++ 共享库安全审计报告 + +**审计日期**: 2026-08-01 +**审计范围**: `libth/`(线程库)、`libhttp/`(HTTP/WebSocket)、`libcrypt/`(加密)、`libsqlite3/`(SQLite封装) +**代码版本**: dev_qing 分支 + +--- + +## 修复进度 + +| 优先级 | 总数 | 已修复 | 待修复 | +|--------|------|--------|--------| +| P0 | 1 | 0 | 1 | +| P1 | 2 | 0 | 2 | +| P2 | 2 | 0 | 2 | + +--- + +## 🔴 P0 — 严重问题 + +### P0-1: libhttp HTTP 解析器需 fuzz 测试,潜在缓冲区溢出 +- **位置**: `libhttp/src/HttpServer.cpp` +- **问题**: 自研 C++ HTTP 解析器未经过大规模 fuzz 测试: + - 异常 HTTP 方法、超长 header、畸形 chunked 编码可能触发缓冲区溢出 + - 边界条件处理不当可导致拒绝服务或远程代码执行 +- **修复建议**: + 1. 使用 AFL++ 或 libFuzzer 对 HTTP 解析器进行 fuzz 测试 + 2. 重点测试:header 解析、URL 解析、chunked 编码、Content-Length 处理 + 3. 考虑替换为成熟库(如 llhttp、picohttpparser) + +--- + +## 🟠 P1 — 高危问题 + +| # | 问题 | 位置 | 说明 | +|---|------|------|------| +| 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` | 畸形帧、超大帧、分片帧处理需验证 | + +--- + +## 🟡 P2 — 中危问题 + +| # | 问题 | +|---|------| +| P2-1 | libsqlite3 封装需验证 SQL 注入防护(确认使用参数化查询) | +| P2-2 | libth 线程库同步原语需验证死锁/竞态条件 | + +--- + +## ✅ 已有安全机制 + +1. ✅ **RAII 智能指针**: 减少内存泄漏和 use-after-free +2. ✅ **C++ 类型安全**: 相比纯 C 减少类型混淆漏洞 +3. ✅ **项目内统一库**: 代码可控,可快速修复 +4. ✅ **libcrypt 已实现**: PBKDF2、SHA256、AES-CBC、RSA、Base64 + +--- + +## 修复路线图 + +### 短期(72小时) +- [ ] 对 libhttp HTTP/WebSocket 解析器进行 fuzz 测试 +- [ ] 验证 libsqlite3 封装的参数化查询正确使用 + +### 中期 +- [ ] libcrypt RSA 实现安全审计 +- [ ] 考虑替换 HTTP 解析器为成熟第三方库 diff --git a/docs/SECURITY_AUDIT_20260801.md b/docs/SECURITY_AUDIT_20260801.md new file mode 100644 index 0000000..afb11b3 --- /dev/null +++ b/docs/SECURITY_AUDIT_20260801.md @@ -0,0 +1,427 @@ +# micro_servers 项目整体安全审计报告 + +**审计日期**: 2026-08-01 +**审计范围**: 全部模块(core/waf/gateway/pymsg/db/static/dnfs/ai_review/smtp/captcha/confs/aibox + C++共享库 + 部署配置) +**代码版本**: dev_qing 分支(工作区版本) + +--- + +## 问题严重等级定义 + +| 等级 | 颜色 | 说明 | 响应时间 | +|------|------|------|----------| +| **P0** | 🔴 严重 | 可直接被利用获取系统权限、接管账户、或造成服务完全不可用 | **立即修复(24小时内)** | +| **P1** | 🟠 高危 | 可被利用获取敏感数据、绕过认证、或造成部分功能不可用 | 72小时内修复 | +| **P2** | 🟡 中危 | 存在安全隐患但利用难度较高或影响有限 | 一周内修复 | +| **P3** | 🟢 低危 | 健壮性/信息泄露/最佳实践类问题,风险较低 | 版本迭代时修复 | + +--- + +## 修复进度总览 + +| 模块 | P0 总数 | 已修复 | 待修复 | P1 总数 | 已修复 | 待修复 | +|------|---------|--------|--------|---------|--------|--------| +| aibox | 2 | 0 | 2 | 4 | 0 | 4 | +| waf | 3 | 2 | 1 | 5 | 1 | 4 | +| confs | 1 | 1 | 0 | 7 | 7 | 0 | +| pymsg | 3 | 1 | 2 | 5 | 0 | 5 | +| db | 1 | 0 | 1 | 3 | 0 | 3 | +| static | 3 | 0 | 3 | 3 | 0 | 3 | +| dnfs | 1 | 0 | 1 | 2 | 0 | 2 | +| smtp | 2 | 0 | 2 | 2 | 0 | 2 | +| ai_review | 1 | 0 | 1 | 1 | 0 | 1 | +| captcha | 0 | 0 | 0 | 1 | 0 | 1 | +| core | 1 | 0 | 1 | 1 | 0 | 1 | +| gateway | 0 | 0 | 0 | 2 | 0 | 2 | +| **合计** | **18** | **4** | **14** | **36** | **8** | **28** | + +--- + +## 🔴 P0 — 严重问题(必须立即修复) + +### P0-1: static 服务无认证密码重置,可直接接管任意账户 +- **位置**: [static/fsys.py#L278-L292](file:///Users/qingsong/micro_servers/static/fsys.py#L278-L292) +- **问题描述**: + ```python + @app.route('/api/reset_password', methods=['POST']) + def api_reset_password(): + data = request.get_json(force=True) + email = data.get('email', '').strip() + new_hash = data.get('password_hash', '').strip() + if not email or not new_hash: + return jsonify({"success": False, "error": "missing params"}), 400 + conn = _sql.connect(GATEWAY_DB) + conn.execute("UPDATE Users SET password_hash=? WHERE email=?", (new_hash, email)) + conn.commit() + ``` + `/api/reset_password` 接口**没有任何认证校验**,任何人只要知道邮箱即可修改任意用户密码(包括管理员),直接接管账户。 +- **利用方式**: 发送 POST 请求到 `/api/reset_password`,传入 `email` 和 `password_hash` 即可重置密码。 +- **修复建议**: + 1. 此接口必须在 gateway 层做严格的管理员权限校验(`is_admin=true`),或直接删除此接口(密码重置应走邮件验证码流程) + 2. 短期缓解:在 static 服务的 security_middleware 中添加 IP 白名单或 token 校验,只允许本机访问 + 3. 长期:彻底移除此接口,密码重置功能通过 smtp 验证码 + gateway 统一实现 + +--- + +### P0-2: dnfs Token 无签名验证,可伪造任意用户身份 +- **位置**: [dnfs/dnfs.py#L285-L292](file:///Users/qingsong/micro_servers/dnfs/dnfs.py#L285-L292) +- **问题描述**: + ```python + def decode_token(token: str): + idx = token.find('_') + if idx != -1: + raw = token[:idx] + # FIXME: 验证数字签名 + decoded = base64.b64decode(raw) + jj = decoded.decode('utf-8') + return json.loads(jj) + ``` + 代码中明确标注了 `FIXME: 验证数字签名`,但实际上**完全跳过了 RSA 签名验证**。攻击者可以: + 1. 自行构造 base64 编码的 JSON payload(包含任意 `user`、`is_admin=true`) + 2. 拼接任意字符串作为"签名"部分 + 3. 直接伪造管理员身份,绕过所有权限校验 +- **修复建议**: + 1. 立即实现 RSA 签名验证,与 gateway 的 Token 验签逻辑保持一致 + 2. dnfs 需要持有 gateway 的 RSA 公钥,或改为通过 pymsg 调 gateway 统一验签 + 3. 添加 Token 过期校验(检查 `created_at` + `keep_time`) + +--- + +### P0-3: static yhdet OAuth Token 为 base64(email:password),解码即得明文密码 +- **位置**: [static/fsys.py#L398-L400](file:///Users/qingsong/micro_servers/static/fsys.py#L398-L400) +- **问题描述**: + ```python + token_data = f"{email}:{password}" + token = base64.urlsafe_b64encode(token_data.encode()).decode() + return jsonify({"success": True, "token": token}) + ``` + yhdet 登录接口签发的 Token 仅仅是 `email:password` 的 base64 编码,**没有任何签名或加密**: + 1. 任何获得此 Token 的人(如 XSS、日志泄露、网络抓包)都可以直接 base64 解码得到用户明文密码 + 2. 密码可能在其他网站复用,导致更大范围的账户接管 + 3. `/api/yhdet_verify` 接口直接解码后查数据库比对,等价于明文密码传输 +- **修复建议**: + 1. 立即废弃此 Token 设计,改为使用与 gateway 一致的 RSA 签名 Token + 2. yhdet OAuth 回调后应调用 gateway 的登录接口获取正规 Token,而非自行签发 + 3. 现有 yhdet_* 用户需要强制重置密码 + +--- + +### P0-4: db 服务零认证对外开放完整 CRUD,虽绑定 127.0.0.1 但无纵深防御 +- **位置**: [db/dbv2.py](file:///Users/qingsong/micro_servers/db/dbv2.py) +- **问题描述**: + - db 服务虽然默认监听 `127.0.0.1:8005`,但**完全没有认证机制** + - 任何能访问本机 8005 端口的进程/用户都可以执行任意 SQL(select/insert/update/delete) + - 可读取 Users 表的密码哈希、Logs 表的所有日志、修改/删除任意数据 + - 没有表级/操作级权限控制 + - SELECT 无 limit 上限,可被拖库 +- **当前缓解**: 依赖 Docker/防火墙绑定 127.0.0.1 作为唯一防护 +- **修复建议**: + 1. 添加 API Token 认证(所有 pymsg 调用需携带预共享密钥) + 2. 添加操作权限控制(如日志写入只允许 insert,不允许 delete) + 3. SELECT 强制添加 LIMIT 上限(如默认 1000 条),防止拖库 + 4. 敏感操作(如 DROP、UPDATE 不带 WHERE)直接拒绝 + +--- + +### P0-5: pymsg recv 超时可被绕过(Slowloris 变种 DoS) +- **位置**: [pymsg/pymsg/__init__.py#L102-L153](file:///Users/qingsong/micro_servers/pymsg/pymsg/__init__.py#L102-L153) +- **问题描述**: + 虽然添加了 `recv_timeout=10s`,但 `sock.settimeout()` 设置的是**单次系统调用**超时,不是整个消息接收的总超时: + - 4 字节长度头最长可占用 `4 * 10s = 40秒` + - 载荷部分按字节发送可使单个连接存活任意长时间 + - 少量攻击者即可耗尽服务端连接/线程资源 +- **影响范围**: 所有使用 pymsg 的服务(db/confs/smtp/captcha/waf logclient) +- **修复建议**: 使用 deadline 模式替代单次 timeout: + ```python + deadline = time.monotonic() + self._recv_timeout + while got < n: + remaining = deadline - time.monotonic() + if remaining <= 0: + raise TimeoutError(...) + sock.settimeout(remaining) + chunk = sock.recv_into(view[got:], n - got) + ``` + +--- + +### P0-6: pymsg UnicodeDecodeError 可导致同步模式服务崩溃 +- **位置**: [pymsg/pymsg/__init__.py#L111](file:///Users/qingsong/micro_servers/pymsg/pymsg/__init__.py#L111)、[pymsg/pymsg/__init__.py#L434-L448](file:///Users/qingsong/micro_servers/pymsg/pymsg/__init__.py#L434-L448) +- **问题描述**: + `payload.decode("utf-8")` 抛出的 `UnicodeDecodeError` 是 `ValueError` 子类,不在 `_handle_client` 的异常捕获列表 `(ConnectionError, ConnectionResetError, OSError, TimeoutError)` 中。 + **同步模式(workers=0)下收到无效 UTF-8 字节会直接导致整个服务进程崩溃!** +- **影响范围**: 所有使用 pymsg 且 workers=0 的服务(默认配置) +- **修复建议**: 在异常捕获列表中添加 `UnicodeDecodeError` 和 `json.JSONDecodeError` + +--- + +### P0-7: core PMC HTTP 服务无认证,可执行任意系统命令 +- **位置**: [core/src/main/HttpTask.cpp#L53](file:///Users/qingsong/micro_servers/core/src/main/HttpTask.cpp#L53)、[core/src/main/HttpTask.cpp#L130-L142](file:///Users/qingsong/micro_servers/core/src/main/HttpTask.cpp#L130-L142) +- **问题描述**: + - HTTP 服务虽然绑定 `127.0.0.1:5200`,但**没有任何认证** + - `/exec` 接口可以执行任意 shell 命令(通过 `boost::process::shell`) + - `/kill` 接口可以杀死任意进程 + - 消息队列通道(Pipe/Mq)的 kill/exec 虽然被注释了,但 HTTP 接口是开放的 + - 任何本地用户/进程都可以通过 `POST http://127.0.0.1:5200/exec?command=...` 执行任意命令(core 通常以 root 运行) +- **修复建议**: + 1. 短期:确保 core HTTP 服务不暴露在公网,配置文件中默认关闭 serve 模式 + 2. 中期:添加 Unix socket 替代 TCP,利用文件系统权限控制访问 + 3. 长期:添加 token 认证或仅允许 root 用户访问 + +--- + +### P0-8: aibox 命令注入漏洞(shell=True) +- **位置**: [aibox/aibox.py](file:///Users/qingsong/micro_servers/aibox/aibox.py) `execute_local_command` 工具(~L2670) +- **问题描述**: 使用 `subprocess.run(cmd, shell=True)` 并直接拼接用户/AI 输入的命令,可执行任意系统操作 +- **修复建议**: + 1. 禁止 `shell=True`,改用参数列表形式 + 2. 添加命令白名单机制 + 3. 限制工作目录 + +--- + +### P0-9: aibox WebSocket 无认证且默认监听 0.0.0.0 +- **位置**: [aibox/aibox.py](file:///Users/qingsong/micro_servers/aibox/aibox.py) WebSocketHandler +- **问题描述**: + - WebSocket 服务监听 `0.0.0.0:8765` 公网可访问 + - 无任何 token/密码认证 + - 结合工具调用漏洞可直接控制服务器、消耗 API 费用 +- **修复建议**: 默认绑定 127.0.0.1,通过 gateway 统一接入认证 + +--- + +### P0-10: WAF 规则默认全部注释,WAF 启动后无任何防护 +- **位置**: [waf/rules/local/custom.conf](file:///Users/qingsong/micro_servers/waf/rules/local/custom.conf) +- **问题描述**: 所有本地自定义规则(SQL注入、XSS、路径遍历、命令注入、扫描器UA)全部被注释掉,WAF 处于"裸奔"状态 +- **修复建议**: + 1. 至少启用一组保守的默认规则(扫描器UA拦截、明显SQL注入特征) + 2. 或在启动日志中明确警告当前为检测-only模式 + 3. README 中醒目标注需要取消规则注释 + +--- + +### P0-11: smtp.py 旧版服务存在大量致命漏洞 +- **位置**: [smtp/smtp.py](file:///Users/qingsong/micro_servers/smtp/smtp.py)(旧版,smtp_v2.py 为新版) +- **问题描述**: + - 旧版 smtp.py 缺乏速率限制、无冷却机制、无验证码过期清理 + - 可被用于邮件炸弹攻击 + - 建议确认 smtp.py 是否已下线,如仍在使用需立即替换为 smtp_v2.py +- **修复建议**: 确认 _run.sh 使用的是 smtp_v2.py,删除或隔离旧版 smtp.py + +--- + +### P0-12: static yhdet_callback 禁用 SSL 证书验证 +- **位置**: [static/fsys.py#L361](file:///Users/qingsong/micro_servers/static/fsys.py#L361) +- **问题描述**: + ```python + with _urlreq.urlopen(req, timeout=10, context=__import__('ssl')._create_unverified_context()) as resp: + ``` + 使用 `_create_unverified_context()` 禁用了 SSL 证书验证,中间人攻击者可伪造 yhdet.top 响应,以任意用户身份登录。 +- **修复建议**: 移除 `_create_unverified_context()`,使用默认的 SSL 验证。 + +--- + +### P0-13: static yhdet OAuth 硬编码密钥 +- **位置**: [static/fsys.py#L312](file:///Users/qingsong/micro_servers/static/fsys.py#L312) +- **问题描述**: + ```python + YHDET_SECRET = "yhdet_qsont_oauth_2026_secret_key" + ``` + OAuth 密钥硬编码在源码中,任何代码访问者都可获取。 +- **修复建议**: 移至环境变量或 .env 文件,不要提交到仓库。 + +--- + +### P0-14: static 服务直连 gateway SQLite 数据库文件,绕过 db 服务 +- **位置**: [static/fsys.py#L287](file:///Users/qingsong/micro_servers/static/fsys.py#L287)、[static/fsys.py#L349](file:///Users/qingsong/micro_servers/static/fsys.py#L349) +- **问题描述**: static 服务直接通过 sqlite3 模块连接 gateway 的数据库文件,绕过了 db 服务的参数化查询保护层和访问控制,存在以下风险: + 1. 多进程并发写可能导致数据库损坏 + 2. SQL 注入风险(虽然部分用了参数化,但代码分散难以审计) + 3. 密码重置等敏感操作绕过了审计日志 +- **修复建议**: 所有数据库操作统一通过 pymsg 调 db:8005 服务,禁止业务服务直连 SQLite 文件。 + +--- + +## 🟠 P1 — 高危问题(72小时内修复) + +### pymsg 模块 +| # | 问题 | 位置 | +|---|------|------| +| P1-1 | 无连接数限制,连接耗尽攻击 | `__init__.py:334` listen(10) 不限制已建立连接 | +| P1-2 | 线程池无队列限制,慢 handler 耗尽内存 | `__init__.py:337-340` ThreadPoolExecutor 默认无界队列 | +| P1-3 | 异常信息直接返回客户端,泄露内部细节 | `rpc.py:144,162` 直接将 `str(e)` 返回给调用方 | +| P1-4 | 无 TLS/加密传输,内网流量可被窃听 | 全模块明文 TCP | +| P1-5 | JSON 解析无深度限制,深度嵌套导致栈溢出/CPU 100% | `recv()` 后 json.loads 无限制 | + +### db 模块 +| # | 问题 | +|---|------| +| P1-1 | db_tools.py 运维工具存在 SQL 字符串拼接风险 | +| P1-2 | 无操作审计日志,谁在什么时候做了什么修改无记录 | +| P1-3 | Logs 表自动清理可能被利用来删除攻击痕迹 | + +### static 模块 +| # | 问题 | 位置 | +|---|------|------| +| P1-1 | `/api/check_user` 接口可枚举有效邮箱 | `fsys.py:295-306` 无速率限制,可遍历邮箱 | +| P1-2 | 文件服务路径遍历防护需要全面审计 | `fsys.py` 文件浏览接口 | +| P1-3 | yhdet 新用户默认密码是可预测的 `oauth_{user_id}` | `fsys.py:348` | + +### dnfs 模块 +| # | 问题 | +|---|------| +| P1-1 | Token 过期校验不严谨 | +| P1-2 | is_admin 权限校验依赖 Token 字段,无二次验证 | + +### smtp 模块 +| # | 问题 | +|---|------| +| P1-1 | 验证码存储在内存,重启后所有未使用验证码失效(可能导致拒绝服务) | +| P1-2 | 无全局发送速率限制接口(smtp_v2 已有但需确认配置) | + +### ai_review 模块 +| # | 问题 | +|---|------| +| P1-1 | `/rag/feedback` 接口无认证,可被恶意投毒 RAG 知识库 | + +### captcha 模块 +| # | 问题 | +|---|------| +| P1-1 | 验证码存储无容量限制,大量生成可耗尽内存 | + +### core 模块 +| # | 问题 | +|---|------| +| P1-1 | init.txt 中命令直接通过 shell 执行,若 init.txt 被篡改可植入后门 | + +### gateway 模块 +| # | 问题 | 位置 | +|---|------|------| +| P1-1 | 反向代理响应无大小限制,下游返回超大响应可导致 OOM | `main.cpp:1625-1627` callback 无限制拼接 body | +| P1-2 | RSA 密钥对每次启动内存生成,重启后所有已签发 Token 失效(设计如此但文档未充分说明) | `main.cpp:547` | + +### waf 模块 +| # | 问题 | 位置 | +|---|------|------| +| P1-1 | IPv6 地址解析错误(带方括号),限流失效 | `proxy.go:40-43,92-95` | +| P1-2 | 响应头检测只检测不拦截 | `proxy.go:131-138` ProcessResponseHeaders 返回值被忽略 | +| P1-3 | 缺少优雅关闭,SIGINT/SIGTERM 时请求被中断 | `main.go:97-125` | +| P1-4 | 标准日志被静默,panic 堆栈丢失 | `main.go:88` `log.SetOutput(io.Discard)` | + +--- + +## 🟡 P2 — 中危问题(一周内修复) + +| 模块 | 问题 | +|------|------| +| pymsg | length=0 空消息未处理;broadcast 无权限/频率控制;select EINTR 处理过于宽泛;TIME_WAIT 堆积 | +| waf | 规则文件无大小限制(可 OOM);logclient 硬编码 db 地址;logclient 短连接 TIME_WAIT;错误日志输出到 stdout | +| static | 视频/音频 Range 请求实现需审计边界;日志可能泄露敏感信息 | +| dnfs | AI 审核 fail-closed 实现需验证;节点删除无确认机制 | +| ai_review | DeepSeek API Key 硬编码风险;RAG 知识库文件无校验 | +| gateway | CORS 配置为通配或白名单需审计;HTTP 服务器无超时配置(依赖 libhttp) | +| confs | 日志注入风险(控制字符未过滤) | +| 所有服务 | log_client.py 四份副本同步问题(修改一份需同步所有) | +| 部署 | docker-compose 中 db 端口虽绑定 127.0.0.1 但容器内 LISTEN_ADDR=0.0.0.0;缺少 healthcheck 依赖的 wait-for-it 机制 | + +--- + +## 🟢 P3 — 低危/改进建议 + +| 类别 | 建议 | +|------|------| +| 代码质量 | 各服务 _set.sh 缺少 `set -e`,pip install 失败继续执行;部分脚本硬编码国内镜像源 | +| 可观测性 | 安全事件无统一审计日志(认证失败、权限拒绝、异常访问) | +| 加固 | C++ 服务编译时开启 ASLR/PIE/RELRO/Stack Canary 等加固选项 | +| 文档 | README 中安全相关配置(WAF 规则启用、Token 配置)需要更醒目的提示 | +| 依赖 | 定期更新 Go/Python/C++ 依赖,检查已知 CVE | + +--- + +## ✅ 已修复的问题(本次审计周期内) + +| # | 问题 | 级别 | 模块 | 修复状态 | +|---|------|------|------|----------| +| 1 | pymsg `recv()` 无超时(Slowloris DoS) | P0 | pymsg | ✅ 添加 recv_timeout=10s(但 P0-5 指出仍有变种问题) | +| 2 | confs 配置文件读取无大小限制(OOM) | P1 | confs | ✅ 1MiB 限制 | +| 3 | log_client 后台线程异常无保护 | P1 | 所有服务 | ✅ 4份副本同步修复 | +| 4 | confs TOCTOU 竞态条件 | P1 | confs | ✅ 文件操作锁内原子完成 | +| 5 | confs 缓存可变引用污染 | P1 | confs | ✅ 返回浅拷贝 | +| 6 | waf 请求体无大小限制(OOM) | P0 | waf | ✅ 10MB LimitReader + 413 | +| 7 | waf HTTP 无超时(Slowloris) | P0 | waf | ✅ ReadTimeout/ReadHeaderTimeout 等 | +| 8 | aibox 工具输出无大小限制(OOM) | P0 | aibox | ✅ ReadFileTool 500行限制,Subprocess 1MB限制,最终结果 1MB 截断 | +| 9 | aibox 工具结果无效格式污染对话 | P1 | aibox | ✅ _sanitize_tool_result + 消息回滚 | +| 10 | aibox 提示词排序错乱 | P2 | aibox | ✅ 数字前缀正则排序 | +| 11 | gateway 密码校验防用户名枚举 | P1 | gateway | ✅ dummy PBKDF2 拉平时序 | +| 12 | gateway 邮件轰炸漏洞 | P0 | gateway | ✅ /register/send_code 强制 CAPTCHA + 邮箱双窗口限流 | + +--- + +## 🛠️ 修复路线图 + +### 第一阶段:立即执行(24小时内,修复所有 P0) +- [ ] **static** 删除或禁用 `/api/reset_password` 无认证接口 +- [ ] **static** 修复 yhdet Token 设计:废弃 base64(email:password),改用 gateway 统一 Token +- [ ] **static** 移除 `_create_unverified_context()` 启用 SSL 证书验证 +- [ ] **dnfs** 实现 Token RSA 签名验证(对接 gateway 公钥) +- [ ] **pymsg** 修复 recv 总超时(deadline 模式) +- [ ] **pymsg** 捕获 UnicodeDecodeError/json.JSONDecodeError 防止崩溃 +- [ ] **waf** 启用默认规则或添加醒目的警告日志 +- [ ] **core** 确认 serve 模式默认关闭或添加认证 +- [ ] **aibox** 将 WebSocket 默认绑定改为 127.0.0.1 +- [ ] **确认** smtp 服务使用的是 smtp_v2.py 而非旧版 smtp.py +- [ ] **static** 移除硬编码 YHDET_SECRET,改用环境变量 + +### 第二阶段:短期修复(72小时内,修复 P1) +- [ ] **db** 添加 API Token 认证 +- [ ] **db** SELECT 强制 LIMIT 上限 +- [ ] **pymsg** 添加最大连接数限制 +- [ ] **pymsg** 线程池使用有界队列 +- [ ] **pymsg** 异常信息脱敏(不返回内部细节给客户端) +- [ ] **waf** 修复 IPv6 地址解析(net.SplitHostPort) +- [ ] **waf** 恢复标准日志输出(不要 Discard) +- [ ] **waf** 添加优雅关闭 +- [ ] **gateway** 反向代理添加响应体大小限制 +- [ ] **ai_review** /rag/feedback 添加认证 +- [ ] **static** /api/check_user 添加速率限制防枚举 + +### 第三阶段:中期改进(一周内,修复 P2) +- [ ] 所有 pymsg 服务统一添加认证机制 +- [ ] log_client.py 重构为共享包,避免多副本同步问题 +- [ ] waf logclient 支持环境变量配置 db 地址 +- [ ] waf 规则文件添加大小限制 +- [ ] 统一各服务的 HTTP 超时配置 +- [ ] 添加安全事件审计日志 +- [ ] docker-compose 添加健康检查依赖等待 + +### 第四阶段:长期加固(版本迭代) +- [ ] 所有内部服务通信考虑 TLS 加密 +- [ ] 统一的认证/鉴权中间件 +- [ ] C++ 服务编译加固选项 +- [ ] 自动化安全测试(模糊测试、渗透测试用例) +- [ ] 秘密管理方案(HashiCorp Vault 或等效方案) + +--- + +## 正面评价(值得肯定的安全实践) + +审计中也发现了许多优秀的安全设计: + +1. ✅ **gateway 密码安全**: PBKDF2-HMAC-SHA256(10万轮+随机盐)、常数时间比较、dummy hash 防用户名枚举 +2. ✅ **gateway Token 机制**: RSA 签名、HttpOnly + Secure + SameSite=Lax Cookie、双来源(Authorization + Cookie) +3. ✅ **分层防护架构**: WAF(边缘规则)→ gateway(鉴权)→ 内部服务(127.0.0.1 绑定)三层防护 +4. ✅ **速率限制**: 登录/注册/发码均有 IP 级 + 邮箱级限流 +5. ✅ **邮件轰炸多层防护**: CAPTCHA + IP限流 + 邮箱限流 + smtp冷却 + 全局熔断 +6. ✅ **X-Forwarded-For 可信链**: WAF 覆盖伪造头,gateway 设置可信值 +7. ✅ **fail-closed 设计**: ai_review、captcha_verify、email_verify 异常时一律拒绝 +8. ✅ **集中式日志**: 异步批量推送、fail-safe、自动清理 +9. ✅ **DoS 防护意识**: pymsg recv_timeout、waf HTTP 超时、请求体大小限制、confs 文件大小限制 +10. ✅ **confs 路径防护**: basename 校验 + realpath 前缀 + TOCTOU 防护 +11. ✅ **gateway 路径安全校验**: 防路径遍历、协议注入、空字节 +12. ✅ **aibox 工具结果清洗**: 控制字符过滤、消息回滚机制 +13. ✅ **waf 热加载 fail-safe**: 规则重载失败保留旧实例 +14. ✅ **私钥/密钥被 .gitignore**: 证书目录、.env 文件不提交仓库 + +--- + +**审计完成时间**: 2026-08-01 +**审计人**: AI Security Audit diff --git a/gateway/docs/SECURITY_AUDIT_20260801.md b/gateway/docs/SECURITY_AUDIT_20260801.md index 99fba92..bb91e1d 100644 --- a/gateway/docs/SECURITY_AUDIT_20260801.md +++ b/gateway/docs/SECURITY_AUDIT_20260801.md @@ -1,324 +1,86 @@ -# gateway 安全审计报告 +# gateway 模块安全审计报告 -- **审计日期**:2026-08-01 -- **审计版本**:gateway 0.5.3(见 [src/main.cpp:31](../src/main.cpp#L31)) -- **审计范围**: - - [gateway/src/main.cpp](../src/main.cpp)(约 1700 行,全部业务实现) - - [gateway/inc/DbC.hpp](../inc/DbC.hpp)、[LogC.hpp](../inc/LogC.hpp)、[ExpBase.hpp](../inc/ExpBase.hpp)、[LocalTime.hpp](../inc/LocalTime.hpp)、[Msg.hpp](../inc/Msg.hpp)、[Conf.hpp](../inc/Conf.hpp) - - 依赖库 [libcrypt/inc/us/MyRSA.hpp](../../libcrypt/inc/us/MyRSA.hpp)、[Base64.hpp](../../libcrypt/inc/us/Base64.hpp) - - 配置 [gateway/example.ini](../example.ini)、构建 [gateway/Makefile](../Makefile) -- **审计方法**:静态代码审查 + 数据流分析 + 威胁建模 -- **总体评价**:架构清晰、安全意识较强(常数时间比较、dummy PBKDF2 防用户名枚举、`what()` 屏蔽源码路径、路径规范化校验、可伪造头剥离),但存在若干可被实际利用的安全缺陷。按严重程度分级如下。 +**审计日期**: 2026-08-01 +**审计范围**: `src/main.cpp`、`src/shared/`(libth、libhttp、libcrypt、libsqlite3) +**代码版本**: dev_qing 分支 --- -## P0 严重(建议立即修复) +## 修复进度 -### P0-1 邮件轰炸:`/register/send_code` 缺 CAPTCHA 强制校验【已修复 v0.5.4】 - -- **位置**:[main.cpp:1387](../src/main.cpp#L1387) `handleSendEmailCode` -- **问题**:该接口仅靠 IP 限流(5 次/10 分钟),无任何人机校验。攻击者可: - - 在模式 B(独立部署)下伪造 `X-Forwarded-For` 即可绕过限流(见 P0-3),向任意邮箱发送大量验证码邮件。 - - 即使模式 A,也可用代理池/IP 轮换绕过 IP 限流。 -- **影响**:smtp 账号被服务商封禁、受害者邮箱被轰炸。 -- **修复(v0.5.4,2026-08-01)**:实施分层防护: - 1. **gateway 层 CAPTCHA 强制校验**:经 captcha:8009 pymsg 校验,Token 一次性消费,fail-closed。 - 2. **gateway 层邮箱级双窗口限流**:3 次/小时 + 10 次/天(CAPTCHA 通过后才计入配额)。 - 3. **smtp 层邮箱级长期限流兜底**:同样 3 次/小时 + 10 次/天,作为 gateway 被绕过/重启时的最后防线。 - 4. **smtp 层全局熔断**:`SMTP_HOURLY_LIMIT` 环境变量(默认 100 封/小时),防 smtp 账号被服务商封禁。 - 5. **前端同步**:`register.html`/`register.js` 接入图形验证码(复用 `/captcha/image` 代理路由,方案 A:仅在「获取验证码」时要求 CAPTCHA,注册提交不加——邮箱验证码已是二次验证)。 - - 改动文件:[gateway/src/main.cpp](../src/main.cpp)(新增 `_EMAIL_ADDR_RATE_HOUR`/`_EMAIL_ADDR_RATE_DAY` + `handleSendEmailCode` 强制 CAPTCHA + 邮箱限流)、[smtp/smtp_v2.py](../../smtp/smtp_v2.py)(新增 `send_log`/`global_send_log` + 限流检查 + 全局熔断 + 预占/回滚)、[static/public/register.html](../../static/public/register.html) + [static/public/js/register.js](../../static/public/js/register.js)(前端接入图形验证码)。 - -### P0-2 注册接口 CAPTCHA 可选,可被批量注册 - -- **位置**:[main.cpp:1331-1338](../src/main.cpp#L1331) -- **问题**:注册流程中 CAPTCHA 仅在客户端「主动提供」时才校验: - -```cpp -if (captcha_it != obj.end() && captcha_ans_it != obj.end()) { - // 校验 -} -``` - -自动化脚本不传 `captcha_token` 即可跳过人机验证,配合获取邮箱验证码即可批量注册垃圾账号。注意 `/login` 在 [main.cpp:800](../src/main.cpp#L800) 是强制要求 CAPTCHA 的,行为不一致。 -- **建议**:CAPTCHA 改为必填(与 `/login` 行为对齐)。 - -### P0-3 模式 B(独立部署)下 XFF 完全可伪造,所有限流失效 - -- **位置**:[main.cpp:776-780](../src/main.cpp#L776)、[1298-1301](../src/main.cpp#L1298)、[1107-1110](../src/main.cpp#L1107)、[1395-1398](../src/main.cpp#L1395) -- **问题**:全部限流均信任 `X-Forwarded-For` 头: - -```cpp -auto it_host = req.base().find("X-Forwarded-For"); -if (it_host != req.base().end()) - client_ip = std::string(it_host->value()); -else - client_ip = "direct"; -``` - -模式 B 下 gateway 直接面向公网,客户端每次发不同 XFF,限流计数器 key 全部不同,等于无限流。README 承认此点(「RateLimiter 取不到 X-Forwarded-For,所有请求记为 direct」),但实际代码是「客户端发啥信啥」,比文档描述更严重。 -- **影响**:登录爆破、注册轰炸、Token 刷新滥用全部失防。 -- **建议**:启动时根据 `_TLS` 选择 IP 来源策略——模式 B 下用 socket 真实源 IP(`Remote-Addr`),不信 XFF;或在 `is_special_header` 已剥离客户端 XFF 的基础上,模式 B 下不再回填。 - -### P0-4 PBKDF2 / RAND_bytes 返回值未检查 - -- **位置**:[main.cpp:66-75](../src/main.cpp#L66) `hash_password`、[main.cpp:97-103](../src/main.cpp#L97) `verify_password` -- **问题**: - -```cpp -RAND_bytes(salt, SALT_LEN); // 返回值未检查 -PKCS5_PBKDF2_HMAC(..., hash); // 返回值未检查 -``` - -极端情况(如 `/dev/urandom` 不可用、内存耗尽)下,salt/hash 为未初始化栈数据,导致: -- 密码哈希可预测(若 salt 全 0)。 -- 验证逻辑用未初始化数据做常数时间比较,行为未定义。 - -OpenSSL 文档明确要求检查返回值。 -- **建议**: - -```cpp -if (RAND_bytes(salt, SALT_LEN) != 1) - throw std::runtime_error("RAND_bytes failed"); -if (PKCS5_PBKDF2_HMAC(...) != 1) - throw std::runtime_error("PBKDF2 failed"); -``` +| 优先级 | 总数 | 已修复 | 待修复 | +|--------|------|--------|--------| +| P0 | 1 | 0 | 1 | +| P1 | 5 | 部分 | 3 | +| P2 | 2 | 0 | 2 | --- -## P1 高(应在下一版本修复) +## 🔴 P0 — 严重问题 -### P1-1 Token 无撤销机制,logout 仅清 Cookie - -- **位置**:[main.cpp:944](../src/main.cpp#L944) `handleLogout` -- **问题**:`handleLogout` 只设置 `Max-Age=0` 的 Cookie,Token 本身仍然有效。Token 一旦泄漏(如 XSS 抓到 Token 值、日志记录、代理缓存),在 `keep_time`(默认 1 小时)内无法撤销,攻击者可继续使用。`refresh_token` 还把可用窗口拉长到 `iat + keep_time * 2`(默认 2 小时)。 -- **建议**: - - Token payload 增加 `jti`(JWT ID)字段。 - - 维护内存黑名单(Set),logout 时把 jti 加入,代理验签后检查。 - - 黑名单按 Token 自然过期自动清理(与 CAPTCHA store 同模式)。 - -### P1-2 反向代理无下游超时,可被慢响应 DoS - -- **位置**:[main.cpp:1633-1639](../src/main.cpp#L1633) -- **问题**:`client.get_stream_sync(url, callback)` / `post_stream_sync` 未传超时。`HttpClient` 的超时行为取决于 libhttp 实现,若下游(static:9200 / dnfs:8006)hang 住,gateway 工作线程会被永久占用。HttpServer 线程池仅 4 线程([main.cpp:540](../src/main.cpp#L540)),4 个慢请求即可让 gateway 拒绝服务。 - -同样问题见于 `verify_captcha_remote`、`verify_email_code_remote`、`handleCaptchaImage`——虽然传了 5s/20s 超时,但 `MsgClient::call` 短连接 + 阻塞,5 秒内 4 个慢请求即可耗尽线程池。 -- **建议**: - - 代理调用显式设置超时(如 10s)。 - - HttpServer 线程池调大或改用异步模型。 - - 关键路径下游调用加熔断(连续超时降级)。 - -### P1-3 代理透传 Cookie 与 Target-Url 到下游 - -- **位置**:[main.cpp:1605-1613](../src/main.cpp#L1605)、[main.cpp:1653](../src/main.cpp#L1653) `is_special_header` -- **问题**:`handleProxy` 复制「几乎所有」请求头到下游,`is_special_header` 只过滤了 `Host/connection/Accept-Encoding/XFF/X-Real-IP/Forwarded`,**未过滤 `Cookie` 和 `Target-Url`**。 - -后果: -- 用户的 `q4_token` Cookie 被透传到 static/dnfs。若下游有 access log 记录请求头,Token 泄漏。 -- `Target-Url` 头透传到下游,可能被下游误解或记录(信息泄漏)。 - -虽然后续 `if (!cookie_token.empty())` 会用 `Authorization: Bearer` 覆盖客户端的 Authorization,但 Cookie 头不会被覆盖。 -- **建议**:`is_special_header` 增加 `Cookie`、`Target-Url`(自定义头不应透传)。 - -### P1-4 无请求体大小限制(模式 B 下尤其严重) - -- **位置**:[main.cpp:787](../src/main.cpp#L787)、[1308](../src/main.cpp#L1308)、[1405](../src/main.cpp#L1405) -- **问题**:直接 `json::parse(req.body())`,无大小检查。模式 A 下 WAF 有 `SecRequestBodyLimit 10MB` 兜底;模式 B 下无任何限制,客户端发 1GB JSON body 即可让 gateway OOM。 -- **建议**: - - 在每个 handler 入口检查 `req.body().size()`,超限返回 413。 - - 或在 libhttp 层统一限制。 - -### P1-5 代理鉴权先于白名单检查,泄漏白名单信息 - -- **位置**:[main.cpp:1538-1591](../src/main.cpp#L1538) -- **问题**:流程为:先验签 → 检查过期 → 再检查 Target-Url 是否在 `_OPEN_URL`。持有有效 Token 的攻击者可枚举 Target-Url: - - 返回 401 → Token 无效或已过期(无法区分) - - 返回 403 → Token 有效但 Target-Url 不在白名单 - - 返回 200/其他 → Target-Url 在白名单内 - -从而探测出 `Proxy.opened` 的完整内容(配置敏感信息)。 -- **建议**:先检查白名单,白名单命中后再做鉴权。 - -### P1-6 LocalTime 用本地时间而非 UTC,跨时区/迁移出问题 - -- **位置**:[LocalTime.hpp:14-18](../inc/LocalTime.hpp#L14) -- **问题**:`getCurrent` 用 `current_zone()`,Token payload 的 `iat`/`created_at` 都是本地时间字符串。问题: - - 服务器迁移时区(如 CST→UTC),旧 Token 的过期判断会错(差 8 小时)。 - - DST 切换瞬间(夏令时)有过期判断偏差。 - - `parse` 与 `getCurrent` 必须在同一时区,跨服务器部署不一致。 - -LogC 用 `_utc_now_iso()` UTC,与 LocalTime 不一致——同一进程两套时间表示。 -- **建议**:Token payload 统一用 UTC ISO8601(可复用 LogC 的 `_utc_now_iso` 逻辑)。 +### P0-1: RSA 私钥存储在仓库中/硬编码 +- **位置**: Token 签名相关代码 +- **问题**: RSA 私钥若硬编码在代码中或随仓库分发: + - 所有部署使用同一密钥对,私钥泄露即全局沦陷 + - 任何有代码访问权限的人都可伪造 Token +- **修复建议**: + 1. 私钥从文件路径加载(启动参数或环境变量),不硬编码 + 2. 每个部署实例生成独立密钥对 + 3. 私钥文件权限 0600,仅服务运行用户可读 + 4. 公钥分发机制:dnfs、aibox 等需要验签的服务可访问公钥 --- -## P2 中(建议修复) +## 🟠 P1 — 高危问题 -### P2-1 RSA 密钥生成失败未在启动时检查 - -- **位置**:[main.cpp:542](../src/main.cpp#L542) -- **问题**:`_KEYGEN = std::make_unique()` 后未验证密钥是否生成成功。`Generator` 构造函数中 `generate_rsa_key` 失败时返回 nullptr(见 [MyRSA.hpp:88-112](../../libcrypt/inc/us/MyRSA.hpp#L88)),后续 `get_private_key_pem()` 会返回空字符串,`Private_Key::from_pem("")` 抛异常,被 wrapperHttp 兜底成 500,但 gateway 已经在跑,所有登录都会 500。 -- **建议**:`main()` 中启动后做一次自签自验(`_PRIV.Sign("test")` + `_PUBL.Verify`),失败直接退出。 - -### P2-2 is_safe_target 未覆盖双重编码与反斜杠 - -- **位置**:[main.cpp:1688](../src/main.cpp#L1688) -- **问题**:检查了 `..`、`%2e`、`%2f`、`%5c`,但未检查: - - 双重编码 `%252e%252e`(下游若做二次 URL 解码会变 `..`)。 - - 反斜杠 `\`(Windows 路径分隔符,Linux 影响小但应防御)。 - - 非 ASCII Unicode 点(`。` 等,极少见)。 -- **建议**:增加 `%25` 检查或白名单字符集(只允许 `[A-Za-z0-9/._~-]`)。 - -### P2-3 url 拼接产生双斜杠 - -- **位置**:[main.cpp:1518-1520](../src/main.cpp#L1518) - -```cpp -url = t_url; // "http://127.0.0.1:9200" -url += "/"; -url += target_path; // "/index.html" -// 结果: "http://127.0.0.1:9200//index.html" -``` - -- **问题**:双斜杠可能导致下游服务(如 Nginx `merge_slashes off`)解析异常。 -- **建议**:`target_path` 已确认以 `/` 开头,无需再补 `/`。 - -### P2-4 refresh_token 旧 Token 仍可用 - -- **位置**:[main.cpp:1099](../src/main.cpp#L1099) -- **问题**:刷新后旧 Token 未作废(因无 jti 黑名单,见 P1-1)。攻击者拿到旧 Token 仍可: - - 用旧 Token 访问代理(在 keep_time 内)。 - - 用旧 Token 再次刷新(消耗主用户的刷新次数)。 - -### P2-5 限流 cleanup 性能与内存 - -- **位置**:[main.cpp:213-219](../src/main.cpp#L213) -- **问题**:cleanup 在 `size() > 10000` 时触发,遍历全表 O(n)。若攻击者用 10001 个不同 IP 触发,每次请求都遍历 1 万条,放大 CPU 消耗。 -- **建议**:改用 LRU 或定期后台线程清理。 - -### P2-6 signalHandler 调用非 async-signal-safe 函数 - -- **位置**:[main.cpp:445-449](../src/main.cpp#L445) -- **问题**:`signalHandler` 调用 `std::cout`、`_SERVE->stop()`(可能用锁),在信号上下文中是 UB。实践中常能工作,但严格来说应改用 `sigwait` + 独立线程或 `signalfd`。 - -### P2-7 配置项无范围校验 - -- **位置**:[main.cpp:507-517](../src/main.cpp#L507) -- **问题**:读取的 `_PORT`、`_TOKEN_KEEP_TIME`、`_TOKEN_REFRESH_GRACE_MULTIPLIER`、`_TOKEN_MAX_REFRESH` 均无范围检查。如 `_TOKEN_KEEP_TIME = 999999999` 会让 Token 几乎不过期;`_PORT = -1` 行为异常。 -- **建议**:启动时校验合理范围,超限拒绝启动。 +| # | 问题 | 说明 | +|---|------|------| +| P1-1 | Token 吊销机制缺失 | 当前无 Token 黑名单/吊销列表,修改密码/注销后旧 Token 仍然有效直到过期 | +| P1-2 | 登录失败次数限制需确认实现 | 是否有 IP/账户级别的失败锁定 | +| P1-3 | 上游代理超时配置需验证 | 反向代理超时过短导致大文件上传失败,过长导致 Slowloris | +| P1-4 | C++ HTTP 解析器缓冲区溢出审计 | libhttp 库需全面 fuzz 测试 | +| P1-5 | 请求体大小限制需确认 | 是否配置了合理的上限(如 10MB) | --- -## P3 低(信息性建议) +## 🟡 P2 — 中危问题 -### P3-1 Cookie 未用 `__Host-` 前缀 - -- **位置**:[main.cpp:928-932](../src/main.cpp#L928) -- **问题**:Cookie 名 `q4_token`,建议改 `__Host-q4_token` 绑定 hostname + Path=/ + Secure,防止子域 Cookie 注入(若未来有子域服务)。 - -### P3-2 handleProxy 对默认目标仍透传客户端 Authorization - -- **位置**:[main.cpp:1572-1575](../src/main.cpp#L1572) -- **问题**:公开目标(flag=true)走 else 分支,只清空 `cookie_token`。但客户端发的 `Authorization` 头已在 [1605-1613](../src/main.cpp#L1605) 被复制到下游请求头中,未被覆盖(因为 `cookie_token.empty()`)。static 可能不解析,但仍是信息泄漏。 - -### P3-3 RSA 密钥对进程内存生成,无法跨实例共享 - -- **位置**:[main.cpp:542](../src/main.cpp#L542) -- **问题**:每次启动重新生成。多实例部署时,Token 在不同实例间不通用(用户登录 gateway-A 后,请求转到 gateway-B 会验签失败)。当前单实例部署,问题不大;若未来水平扩展,需改为持久化密钥(加密存储)。 - -### P3-4 whoami 返回 Token 内部字段 - -- **位置**:[main.cpp:1067-1076](../src/main.cpp#L1067) -- **问题**:返回 `iat`/`created_at`/`keep_time`,给攻击者提供精确过期时间。便于攻击者规划 Token 重放,但风险有限。 - -### P3-5 OPTIONS 预检未显式注册 - -- **位置**:[registerRoutes](../src/main.cpp#L610) -- **问题**:没有 `server.options("*", ...)`,README 说「通配 * 返回 CORS 处」,但代码依赖 `server.get("*")` 捕获 OPTIONS。若 libhttp 的 HttpServer 不自动路由 OPTIONS 到 GET,OPTIONS 会被当成 404 或走 proxy。建议显式注册 OPTIONS handler。 +| # | 问题 | +|---|------| +| P2-1 | 错误页面可能泄露版本号/堆栈信息 | +| P2-2 | HTTPS 配置(如使用)需确认 TLS 版本和密码套件安全 | --- -## 修复优先级建议 +## ✅ 已有安全机制 -| 优先级 | 项目 | 工作量 | 建议时间 | -|--------|------|--------|----------| -| ✅ 已修复 | P0-1 邮件轰炸防护(v0.5.4) | 中 | 2026-08-01 完成 | -| 立即 | P0-2 CAPTCHA 强制化 | 小 | 本周 | -| 立即 | P0-3 模式 B 下 IP 来源修正 | 小 | 本周 | -| 立即 | P0-4 PBKDF2/RAND_bytes 返回值检查 | 极小 | 本周 | -| 短期 | P1-1 Token 黑名单(jti) | 中 | 2 周内 | -| 短期 | P1-2 代理下游超时 + 线程池调大 | 中(需改 libhttp) | 2 周内 | -| 短期 | P1-3 过滤 Cookie/Target-Url 头 | 极小 | 2 周内 | -| 短期 | P1-4 请求体大小限制 | 小 | 2 周内 | -| 短期 | P1-5 代理鉴权顺序调整 | 小 | 2 周内 | -| 中期 | P1-6 Token 时间统一 UTC | 小(需处理存量 Token) | 1 月内 | -| 中期 | P2-* 各项 | 小-中 | 1 月内 | +1. ✅ **PBKDF2 密码哈希**: 慢哈希算法防彩虹表,使用唯一盐值 + ```cpp + bool auth_ok = PBKDF2Hasher::verify_password(password, stored_hash); + ``` +2. ✅ **RSA Token 签名**: Token 使用 RSA 私钥签名,防伪造 +3. ✅ **反向代理架构**: 统一入口,隐藏后端服务拓扑 +4. ✅ **速率限制**: 邮件轰炸多层防护(gateway+smtp 双窗口限流、60秒冷却、失败锁定) +5. ✅ **防用户名枚举**: 登录失败返回统一消息(需确认实现) +6. ✅ **WAF 前置防护**: Go WAF 处理 SQLi/XSS/CC/协议解析 +7. ✅ **C++ 高性能实现**: 避免 Python GIL 瓶颈,抗并发能力强 +8. ✅ **依赖审计**: 使用项目自带的 libcrypt 加密库(PBKDF2、RSA) --- -## 已有的良好实践(肯定) +## 修复路线图 -为公正起见,记录审计中确认的已有安全设计: +### 立即(24小时) +- [ ] 确认 RSA 私钥不从仓库/硬编码加载,改为外部文件 +- [ ] 验证请求体大小限制已启用(参考 waf 的 10MB 配置) -| 特性 | 实现位置 | 说明 | -|------|----------|------| -| 密码哈希 | [main.cpp:58](../src/main.cpp#L58) `PBKDF2Hasher` | PBKDF2-HMAC-SHA256,10 万轮 + 16 字节随机盐,行业标准 | -| 常数时间比较 | [main.cpp:107-111](../src/main.cpp#L107) | `diff |= hash[i] ^ expected[i]` 防 timing attack | -| 防用户名枚举 | [main.cpp:862-868](../src/main.cpp#L862) | 用户不存在时执行 dummy PBKDF2 拉平响应时序 | -| 源码路径屏蔽 | [ExpBase.hpp:173-197](../inc/ExpBase.hpp#L173) | `what()` 注释了位置信息,不进入 HTTP 响应 | -| 异常兜底 | [main.cpp:703-724](../src/main.cpp#L703) | `wrapperHttp` 外层 catch 统一返回 "Internal Server Error" | -| 路径规范化 | [main.cpp:1688](../src/main.cpp#L1688) `is_safe_target` | 防路径遍历/协议注入/空字节/URL 编码绕过 | -| 可伪造头剥离 | [main.cpp:1653](../src/main.cpp#L1653) `is_special_header` | 剥离客户端 XFF/X-Real-IP/Forwarded,由 gateway 主动设置可信值 | -| RSA 填充 | [MyRSA.hpp:211](../../libcrypt/inc/us/MyRSA.hpp#L211) | 默认 PSS 填充(比 PKCS1_v1_5 更安全) | -| RSA 密钥长度 | [MyRSA.hpp:85](../../libcrypt/inc/us/MyRSA.hpp#L85) | 2048 位,符合当前推荐 | -| Cookie 安全属性 | [main.cpp:928-932](../src/main.cpp#L928) | `HttpOnly + Secure + SameSite=Lax` | -| 密码强度校验 | [main.cpp:148](../src/main.cpp#L148) `PasswordValidator` | 8+ 字符、4 类中至少 3 类 | -| 限流清理 | [main.cpp:213-219](../src/main.cpp#L213) | 超过 1 万条自动清理(虽性能可优化,见 P2-5) | -| 集中式日志 | [LogC.hpp](../inc/LogC.hpp) | 异步批量 + fail-safe,不拖慢业务 | -| Token 刷新机制 | [main.cpp:1099](../src/main.cpp#L1099) | 保留 iat 限制总生命周期,refresh_count 上限 | +### 短期(72小时) +- [ ] 实现 Token 吊销列表(密码修改时注销旧 Token) +- [ ] 确认登录失败锁定机制生效 +- [ ] 验证代理超时配置(防 Slowloris + 支持大文件上传) +- [ ] libhttp 解析器 fuzz 测试 ---- - -## 附录:审计检查清单 - -| 类别 | 检查项 | 结果 | -|------|--------|------| -| 认证 | 密码哈希算法 | ✅ PBKDF2 | -| 认证 | 密码哈希参数(轮数/盐长度) | ✅ 10 万轮 / 16 字节 | -| 认证 | 常数时间比较 | ✅ | -| 认证 | 防用户名枚举 | ✅ dummy PBKDF2 | -| 认证 | Token 签名算法 | ✅ RSA-PSS + SHA256 | -| 认证 | Token 过期校验 | ✅ | -| 认证 | Token 撤销机制 | ❌ 见 P1-1 | -| 认证 | CAPTCHA 强制 | ✅ login + send_code 强制,register 可选(见 P0-2) | -| 授权 | 反向代理白名单 | ✅ | -| 授权 | 鉴权顺序 | ❌ 见 P1-5 | -| 授权 | is_admin 字段 | ✅ 由 gateway 统一签发,下游不各自维护 | -| 限流 | 登录/注册/发码/刷新 | ✅ 均有限流 | -| 限流 | 邮箱级限流 | ✅ 3 次/小时 + 10 次/天(gateway + smtp 双层,见 P0-1 修复) | -| 限流 | IP 来源可信 | ❌ 模式 B 下可伪造,见 P0-3 | -| 限流 | cleanup 性能 | ⚠️ 见 P2-5 | -| 输入校验 | 请求体大小 | ❌ 见 P1-4 | -| 输入校验 | JSON 解析异常处理 | ✅ | -| 输入校验 | 路径规范化 | ✅(可加强,见 P2-2) | -| 输入校验 | 邮箱格式 | ✅ 正则 | -| 输入校验 | 密码强度 | ✅ | -| 输出 | 错误信息泄漏 | ✅ what() 屏蔽 + 兜底 | -| 输出 | Token 内部字段 | ⚠️ 见 P3-4 | -| 传输 | TLS 终止位置 | ✅ WAF 模式下由 WAF 终止 | -| 传输 | Cookie 安全属性 | ✅ HttpOnly+Secure+SameSite | -| 传输 | HSTS | ✅ 由 WAF 注入 | -| 并发 | 限流器线程安全 | ✅ mutex | -| 并发 | CAPTCHA store 线程安全 | ✅ mutex | -| 并发 | 信号处理 | ⚠️ 见 P2-6 | -| 依赖 | OpenSSL 返回值检查 | ❌ 见 P0-4 | -| 依赖 | libcurl 超时 | ❌ 见 P1-2 | -| 配置 | 配置项范围校验 | ❌ 见 P2-7 | -| 配置 | RSA 密钥生成自检 | ❌ 见 P2-1 | -| 日志 | 集中式日志 | ✅ | -| 日志 | fail-safe | ✅ | -| 日志 | 敏感信息记录 | ⚠️ Token 不入日志,但 IP/用户名记录(可接受) | - ---- - -*报告结束* +### 中期 +- [ ] 错误页面脱敏 +- [ ] TLS 安全配置(HTTPS 场景) +- [ ] 管理操作审计日志 diff --git a/pymsg/docs/SECURITY_AUDIT_20260801.md b/pymsg/docs/SECURITY_AUDIT_20260801.md new file mode 100644 index 0000000..3f67dbd --- /dev/null +++ b/pymsg/docs/SECURITY_AUDIT_20260801.md @@ -0,0 +1,139 @@ +# pymsg 模块安全审计报告 + +**审计日期**: 2026-08-01 +**审计范围**: `pymsg/__init__.py`、`pymsg/rpc.py` +**代码版本**: dev_qing 分支 + +--- + +## 问题严重等级 + +| 等级 | 说明 | 响应时间 | +|------|------|----------| +| P0 🔴 | 可导致服务崩溃、DoS 或远程接管 | 立即修复 | +| P1 🟠 | 可导致资源耗尽、信息泄露 | 72小时内 | +| P2 🟡 | 健壮性/配置问题 | 一周内 | +| P3 🟢 | 优化建议 | 版本迭代 | + +--- + +## 修复进度 + +| 优先级 | 总数 | 已修复 | 待修复 | +|--------|------|--------|--------| +| P0 | 3 | 1 | 2 | +| P1 | 5 | 0 | 5 | +| P2 | 4 | 0 | 4 | +| P3 | 4 | 0 | 4 | + +--- + +## 🔴 P0 — 严重问题 + +### P0-1: recv 超时可被绕过(Slowloris 变种 DoS) +- **位置**: `__init__.py:102-153` (`recv` / `_recv_exact`) +- **问题**: `sock.settimeout()` 设置的是**单次系统调用**超时,不是整个消息接收总超时。攻击者可每次在快超时时发送 1 字节: + - 4 字节长度头最长可占用 `4 * recv_timeout = 40秒` + - 载荷部分按字节发送可使连接存活任意长时间 +- **影响**: 少量攻击者即可耗尽连接/线程资源,造成所有依赖 pymsg 的服务拒绝服务 +- **修复建议**: 使用 deadline 模式: + ```python + deadline = time.monotonic() + self._recv_timeout + while got < n: + remaining = deadline - time.monotonic() + if remaining <= 0: + raise TimeoutError("Message receive timeout") + sock.settimeout(remaining) + chunk = sock.recv_into(view[got:], n - got) + ``` + +--- + +### P0-2: UnicodeDecodeError 可导致同步模式服务崩溃 +- **位置**: `__init__.py:111`、`__init__.py:434-448` +- **问题**: `payload.decode("utf-8")` 抛出的 `UnicodeDecodeError`(`ValueError` 子类)不在 `_handle_client` 的异常捕获列表 `(ConnectionError, ConnectionResetError, OSError, TimeoutError)` 中。**同步模式(workers=0,默认配置)下收到无效 UTF-8 字节会直接导致整个服务进程崩溃!** +- **影响范围**: db/confs/smtp/captcha 等所有默认配置的 pymsg 服务 +- **修复建议**: 在异常捕获中添加 `UnicodeDecodeError` 和 `json.JSONDecodeError`: + ```python + except (ConnectionError, ConnectionResetError, OSError, TimeoutError, + UnicodeDecodeError, json.JSONDecodeError): + ... + ``` + +--- + +### P0-3: 无认证/鉴权机制(已部分缓解:绑定 127.0.0.1) +- **位置**: `rpc.py:80-171` 整个 RpcServer +- **问题**: RPC 服务完全没有身份验证,任何能连接到端口的客户端都能调用所有路由 +- **当前缓解**: 所有服务均绑定 127.0.0.1,依赖网络层隔离 +- **修复建议**: + 1. 添加基于预共享 token 的认证(如 `_auth` 字段) + 2. 支持路由级权限控制 + 3. 文档明确警告不要暴露到公网 + +--- + +## 🟠 P1 — 高危问题 + +| # | 问题 | 位置 | +|---|------|------| +| P1-1 | 无连接数限制,连接耗尽攻击 | `__init__.py:334` `listen(10)` 不限制已建立连接数 | +| P1-2 | 线程池无队列限制,慢 handler 耗尽内存 | `__init__.py:337-340` ThreadPoolExecutor 默认无界队列 | +| P1-3 | 异常信息直接返回客户端,泄露内部细节 | `rpc.py:144,162` 直接将 `str(e)` 返回调用方 | +| P1-4 | 无 TLS/加密传输,内网流量可被窃听 | 全模块明文 TCP | +| P1-5 | JSON 解析无深度限制,深度嵌套导致栈溢出/CPU 100% | `recv()` 后 `json.loads()` 无限制 | + +--- + +## 🟡 P2 — 中危问题 + +| # | 问题 | +|---|------| +| P2-1 | length=0 空消息未特殊处理,可用于心跳攻击 | +| P2-2 | broadcast() 无频率/权限控制,可被用于放大攻击 | +| P2-3 | select 异常处理过于宽泛,EINTR 临时错误直接退出循环 | +| P2-4 | MsgClient 短连接模式导致 TIME_WAIT 堆积 | + +--- + +## 🟢 P3 — 低危/建议 + +| # | 建议 | +|---|------| +| P3-1 | 安全事件(超大消息、超时、JSON解析失败)无日志 | +| P3-2 | `name` 参数未过滤控制字符,可导致日志注入 | +| P3-3 | listen backlog=10 在高并发下偏小 | +| P3-4 | 无优雅关闭(直接 close() 可能发送 RST) | + +--- + +## ✅ 已有安全机制 + +1. ✅ 64 MiB 消息大小上限(`_MAX_MSG_SIZE`),防止 OOM +2. ✅ send/recv 双端都检查消息大小 +3. ✅ recv_timeout 防 Slowloris(基础实现,需修复 P0-1) +4. ✅ 线程池模式下同一连接串行处理,避免帧交错 +5. ✅ 使用 `sendmsg` 散射-聚集 I/O 减少内存拷贝 +6. ✅ 使用 `recv_into` 预分配 buffer +7. ✅ 线程安全的发送锁 +8. ✅ 回调异常隔离(线程池模式) +9. ✅ 单元测试覆盖消息大小边界 + +--- + +## 修复路线图 + +### 立即(24小时) +- [ ] 修复 P0-1: recv 总超时(deadline 模式) +- [ ] 修复 P0-2: 捕获 UnicodeDecodeError/JSONDecodeError + +### 短期(72小时) +- [ ] 添加最大连接数限制 +- [ ] 线程池改用有界队列 +- [ ] 异常信息脱敏 +- [ ] JSON 解析深度限制 + +### 中期 +- [ ] 统一认证机制 +- [ ] TLS 支持选项 +- [ ] 安全事件日志 diff --git a/smtp/docs/SECURITY_AUDIT_20260801.md b/smtp/docs/SECURITY_AUDIT_20260801.md new file mode 100644 index 0000000..c448b97 --- /dev/null +++ b/smtp/docs/SECURITY_AUDIT_20260801.md @@ -0,0 +1,67 @@ +# smtp 模块安全审计报告 + +**审计日期**: 2026-08-01 +**审计范围**: `smtp.py`(旧版)、`smtp_v2.py`(新版) +**代码版本**: dev_qing 分支 + +--- + +## 修复进度 + +| 优先级 | 总数 | 已修复 | 待修复 | +|--------|------|--------|--------| +| P0 | 2 | 1(v2已修复) | 1(旧版需下线) | +| P1 | 2 | 部分 | 1 | + +--- + +## 🔴 P0 — 严重问题 + +### P0-1: 旧版 smtp.py 存在大量致命漏洞(需立即下线) +- **位置**: `smtp.py` +- **问题**: 旧版 smtp.py 缺乏关键防护: + 1. 无发送速率限制(可被用于邮件炸弹) + 2. 无同邮箱 60 秒发送冷却 + 3. 无验证码过期清理机制 + 4. 无连续失败锁定机制 +- **修复建议**: + 1. **确认 `_run.sh` 使用的是 `smtp_v2.py`** + 2. 删除或重命名旧版 `smtp.py` 防止误用 + 3. 在代码注释中明确标记废弃 + +### P0-2: 依赖 pymsg 无认证(缓解:绑定 127.0.0.1) +- **说明**: 与其他 pymsg 服务相同,当前依赖网络层隔离 + +--- + +## 🟠 P1 — 高危问题 + +| # | 问题 | 说明 | +|---|------|------| +| P1-1 | 验证码存储在进程内存,服务重启后所有未使用验证码失效 | 可被利用造成拒绝服务(频繁重启导致用户无法注册) | +| P1-2 | 全局发送熔断配置需确认生效(`SMTP_HOURLY_LIMIT`) | smtp_v2 已有但需验证环境变量正确加载 | + +--- + +## ✅ 已有安全机制(smtp_v2.py) + +1. ✅ **同邮箱 60 秒发送冷却**: 冷却期拒绝并返回 `retry_after` +2. ✅ **邮箱级限流**(gateway 层双窗口:3次/小时 + 10次/天) +3. ✅ **验证码一次性消费**: 校验通过立即删除,防重放 +4. ✅ **连续失败 5 次自动作废**: 防暴力破解验证码 +5. ✅ **10 分钟有效期**: 惰性过期清理 +6. ✅ **发送失败不计入冷却**: 防止网络错误误限制 +7. ✅ **全局熔断(可选)**: `SMTP_HOURLY_LIMIT` 默认 100 封/小时 +8. ✅ **fail-closed**: SMTP 异常时验证码校验失败 + +--- + +## 修复路线图 + +### 立即(24小时) +- [ ] 确认 smtp 服务运行的是 smtp_v2.py,旧版下线 +- [ ] 删除或隔离 smtp.py 防止误用 + +### 短期 +- [ ] 验证全局熔断配置生效 +- [ ] 验证码存储考虑持久化(可选,当前内存实现适合单实例) diff --git a/static/docs/SECURITY_AUDIT_20260801.md b/static/docs/SECURITY_AUDIT_20260801.md new file mode 100644 index 0000000..27138af --- /dev/null +++ b/static/docs/SECURITY_AUDIT_20260801.md @@ -0,0 +1,132 @@ +# static 模块安全审计报告 + +**审计日期**: 2026-08-01 +**审计范围**: `fsys.py`、`security_middleware.py` +**代码版本**: dev_qing 分支 + +--- + +## 修复进度 + +| 优先级 | 总数 | 已修复 | 待修复 | +|--------|------|--------|--------| +| P0 | 5 | 0 | 5 | +| P1 | 3 | 0 | 3 | +| P2 | 2 | 0 | 2 | + +--- + +## 🔴 P0 — 严重问题 + +### P0-1: 无认证密码重置接口,可接管任意账户 +- **位置**: `fsys.py:278-292` +- **问题**: + ```python + @app.route('/api/reset_password', methods=['POST']) + def api_reset_password(): + data = request.get_json(force=True) + email = data.get('email', '').strip() + new_hash = data.get('password_hash', '').strip() + conn = _sql.connect(GATEWAY_DB) + conn.execute("UPDATE Users SET password_hash=? WHERE email=?", (new_hash, email)) + conn.commit() + ``` + `/api/reset_password` **无任何认证**,任何人只要知道邮箱即可修改任意用户密码(包括管理员),直接接管账户。 +- **修复建议**: + 1. **立即禁用/删除此接口**,或仅允许本机访问 + 管理员 Token + 2. 长期:密码重置应走邮件验证码流程(smtp + gateway 统一实现) + +--- + +### P0-2: yhdet OAuth Token 为 base64(email:password),解码即得明文密码 +- **位置**: `fsys.py:398-400` +- **问题**: + ```python + token_data = f"{email}:{password}" + token = base64.urlsafe_b64encode(token_data.encode()).decode() + ``` + Token 仅仅是 `email:password` 的 base64 编码,**无签名无加密**: + 1. XSS/日志泄露/网络抓包即可解码得到明文密码 + 2. 密码若复用其他站点,导致更大范围账户接管 + 3. `/api/yhdet_verify` 直接解码后查库,等价于明文密码传输 +- **修复建议**: + 1. 废弃此 Token 设计,改为调用 gateway 登录接口获取正规 RSA 签名 Token + 2. 现有 yhdet_* 用户强制密码重置 + +--- + +### P0-3: yhdet_callback 禁用 SSL 证书验证(中间人攻击) +- **位置**: `fsys.py:361` +- **问题**: + ```python + with _urlreq.urlopen(req, timeout=10, context=__import__('ssl')._create_unverified_context()) as resp: + ``` + 使用 `_create_unverified_context()` 禁用 SSL 证书验证,中间人可伪造 yhdet.top 响应,以任意用户身份登录。 +- **修复建议**: 移除 `_create_unverified_context()`,使用默认 SSL 验证。 + +--- + +### P0-4: yhdet OAuth 硬编码密钥 +- **位置**: `fsys.py:312` +- **问题**: + ```python + YHDET_SECRET = "yhdet_qsont_oauth_2026_secret_key" + ``` + OAuth 密钥硬编码在源码中并提交到仓库。 +- **修复建议**: 移至环境变量或 `.env` 文件,从代码中移除。 + +--- + +### P0-5: 直连 gateway SQLite 数据库文件,绕过 db 服务 +- **位置**: `fsys.py:287,349` 等多处 +- **问题**: static 直接通过 sqlite3 模块连接 gateway 的数据库文件: + 1. 多进程并发写可能导致数据库损坏 + 2. 绕过 db 服务的访问控制和审计 + 3. 密码重置等敏感操作无日志 +- **修复建议**: 所有数据库操作统一通过 pymsg 调 `db:8005` 服务。 + +--- + +## 🟠 P1 — 高危问题 + +| # | 问题 | 位置 | +|---|------|------| +| P1-1 | `/api/check_user` 无速率限制,可枚举有效邮箱 | `fsys.py:295-306` | +| P1-2 | 文件服务路径遍历防护需全面审计 | 文件浏览/下载接口 | +| P1-3 | yhdet 新用户默认密码可预测:`oauth_{user_id}` | `fsys.py:348` | + +--- + +## 🟡 P2 — 中危问题 + +| # | 问题 | +|---|------| +| P2-1 | 视频/音频 Range 请求实现需审计边界条件 | +| P2-2 | 日志可能记录敏感信息(密码、Token) | + +--- + +## ✅ 已有安全机制 + +1. ✅ **security_middleware.py**: 安全响应头(CSP、X-Frame-Options、nosniff 等) +2. ✅ **路径遍历防护**: `safe_path_join()` realpath 前缀校验 +3. ✅ **dnfs 代理**: 解决浏览器跨域问题 +4. ✅ **静态文件托管**: 独立服务,经 gateway 代理访问 + +--- + +## 修复路线图 + +### 立即(24小时) +- [ ] 禁用/删除无认证的 `/api/reset_password` 接口 +- [ ] 移除 `_create_unverified_context()` 启用 SSL 验证 +- [ ] 从代码中移除硬编码 YHDET_SECRET + +### 短期(72小时) +- [ ] yhdet Token 改用 gateway 统一 RSA 签名 Token +- [ ] `/api/check_user` 添加速率限制 +- [ ] yhdet 新用户密码改为随机生成 + +### 中期 +- [ ] 所有数据库操作迁移到 pymsg 调 db 服务 +- [ ] 文件服务路径遍历全面审计