docs: 新增 libth 安全审计与 aibox 开放版开发计划
This commit is contained in:
1 parent
fe618be1fa
commit
3460a157d0
3 files changed
+290
No files matched your search
@@ -0,0 +1,194 @@
|
||||
# aibox 开放版开发计划
|
||||
|
||||
## 背景
|
||||
|
||||
当前 aibox 主分支完整功能包含**命令执行工具**(`run_command`),该功能存在 **P0-1 严重命令注入漏洞**([aibox_SECURITY_AUDIT_20260801.md](../security/aibox_SECURITY_AUDIT_20260801.md)):
|
||||
|
||||
- 使用 `shell=True` + 用户输入拼接,可执行任意系统命令
|
||||
- P0-2 WebSocket 无认证,结合可导致远程 RCE
|
||||
- 不适合在公开网络环境下开放使用
|
||||
|
||||
**目标**:创建一个**阉割版 aibox**,移除命令执行、文件操作等危险功能,保留核心对话、技能系统、记忆管理、任务调度,可安全开放使用。
|
||||
|
||||
## 版本分支
|
||||
|
||||
- `main`:保持现有完整功能,供本地可信环境使用
|
||||
- `open`:开放阉割版,移除所有危险功能,安全可公开访问
|
||||
|
||||
## 功能裁剪方案
|
||||
|
||||
| 功能 | 主分支完整版 | 开放阉割版 | 备注 |
|
||||
|------|-------------|------------|------|
|
||||
| AI 对话(流式输出) | ✅ | ✅ | 保留核心能力 |
|
||||
| WebSocket 前端交互 | ✅ | ✅ | 需要认证 |
|
||||
| DeepSeek API 调用 | ✅ | ✅ | 保留 |
|
||||
| 思考模式(Reasoning) | ✅ | ✅ | 保留 |
|
||||
| 子代理 | ✅ | ✅ | 保留(无工具调用) |
|
||||
| 情绪表达(情绪化技能) | ✅ | ✅ | 保留 |
|
||||
| 记忆搜索/保存 | ✅ | ✅ | 保留 |
|
||||
| 备忘录/任务调度 | ✅ | ✅ | 保留 |
|
||||
| 扩展技能系统 | ✅ | ✅ | 只保留 Markdown 技能,移除可执行技能 |
|
||||
| 提示词系统 | ✅ | ✅ | 保留(identity/soul 更新) |
|
||||
| 对话压缩 | ✅ | ✅ | 保留 |
|
||||
| SOCKS5 代理 | ✅ | ✅ | 保留(API/网页访问) |
|
||||
| --- | --- | --- | --- |
|
||||
| `read_file` 读文件 | ✅ | ❌ | 移除,防止任意文件读取(路径遍历 P1-1) |
|
||||
| `write_file` 写文件 | ✅ | ❌ | 移除,防止任意文件写入 |
|
||||
| `list_files` 列目录 | ✅ | ❌ | 移除 |
|
||||
| `run_command` 命令执行 | ✅ | ❌ | **完全移除**,P0-1 根源 |
|
||||
| `simple_web` 网页访问 | ✅ | ✅ | 需要 SSRF 防护(内网 IP 过滤) |
|
||||
| `create_skill` 创建技能 | ✅ | ❌ | 移除(会创建文件) |
|
||||
| `install_skill` 安装技能 | ✅ | ❌ | 移除(会安装可执行) |
|
||||
| `update_identity` / `update_soul` | ✅ | ✅ | 保留,仅修改提示词文件 |
|
||||
| --- | --- | --- | --- |
|
||||
|
||||
## 需要添加的安全措施
|
||||
|
||||
### 1. WebSocket 认证(P0-2 修复)
|
||||
- 绑定地址强制改为 `127.0.0.1`,通过 gateway/WAF 反向代理接入
|
||||
- 握手阶段校验 `token` 查询参数或 `Authorization: Bearer <token>`
|
||||
- token 从配置文件读取,环境变量可覆盖
|
||||
- 不匹配立即关闭连接,不处理任何消息
|
||||
|
||||
### 2. SSRF 防护(P1-2 修复)
|
||||
- `SimpleWebCrawlerTool` 访问网页前解析主机 IP
|
||||
- 禁止访问内网 IP 段:
|
||||
- `127.0.0.0/8`(回环)
|
||||
- `10.0.0.0/8`(私网)
|
||||
- `172.16.0.0/12`(私网)
|
||||
- `192.168.0.0/16`(私网)
|
||||
- `169.254.0.0/16`(链路本地)
|
||||
- `0.0.0.0/8`
|
||||
- `100.64.0.0/10`(共享地址)
|
||||
- 禁止非 HTTP/HTTPS 协议(仅允许 `http://`/`https://`)
|
||||
- 跟随重定向时重新校验目标地址
|
||||
|
||||
### 3. 可执行技能完全禁止
|
||||
- `SkillManager` 只加载 `.md` Markdown 技能
|
||||
- 跳过目录中所有可执行文件(无扩展名或 `.py`/`.sh`)
|
||||
- `search_skills` 只返回 Markdown 技能
|
||||
- 移除 `create_skill`/`install_skill` 工具
|
||||
|
||||
### 4. 文件操作限制
|
||||
- **完全移除** `read_file`/`write_file`/`list_files`
|
||||
- `update_identity`/`update_soul` 只允许修改 `~/.aibox/prompts/` 下固定文件,不允许路径遍历
|
||||
- `check_path_safety` 严格校验文件名,确保在允许目录内
|
||||
|
||||
### 5. 频率限制(P2-1 修复)
|
||||
- 每个连接/每个 token 每分钟限制消息轮数(默认 30 轮/分钟)
|
||||
- 超限返回 `error: rate limit exceeded`
|
||||
|
||||
### 6. 其他加固
|
||||
- 启动时强制校验 `DEEPSEEK_API_KEY` 配置,无效直接退出(P1-4 修复)
|
||||
- 错误信息脱敏,不泄露路径/栈追踪(P1-3 修复)
|
||||
- 日志脱敏,不记录 API Key、token 等敏感信息(P2-6 修复)
|
||||
|
||||
## 代码重构步骤
|
||||
|
||||
### 第一步:工具层移除危险工具
|
||||
1. 在 `DeepSeekChat._register_tools()` 中:
|
||||
- 删除/注释 `ReadFileTool`
|
||||
- 删除/注释 `WriteFileTool`
|
||||
- 删除/注释 `ListFilesTool`
|
||||
- 删除/注释 `CommandLineTool`
|
||||
- 删除/注释 `CreateSkillTool`
|
||||
- 删除/注释 `InstallSkillTool`
|
||||
- 保留:`SearchSkillsTool`、`ReloadSkillsTool`、`AskUserTool`、`ExpressEmotionTool`、`UpdateIdentityTool`、`UpdateSoulTool`、`SaveMemoryAndEndConversationTool`、`MemoTool`、`MemoryTool`、`SimpleWebCrawlerTool`、`GetCurrentTimeTool`、`CalculatorTool`、`SubAgentTool`、`SetProxyTool`、`GetProxyTool`
|
||||
|
||||
### 第二步:修改 SkillManager 只加载 Markdown 技能
|
||||
1. `SkillManager._load_skills()` 中只加载 `.md` 文件
|
||||
2. 跳过所有其他扩展名(包括无扩展名)
|
||||
3. `search_skills` 只返回 Markdown 技能
|
||||
4. 移除执行相关代码路径
|
||||
|
||||
### 第三步:添加 WebSocket 认证
|
||||
1. 在 `handle_connection` 握手时检查 token:
|
||||
- 从 `query` 中获取 `token` 参数,或从 `Authorization: Bearer` 获取
|
||||
- 与配置文件中 `auth.token` 比对
|
||||
- 不匹配:关闭连接,返回 1008 错误
|
||||
2. 配置新增 `auth.token` 项,必填
|
||||
3. 默认监听地址改为 `127.0.0.1`(原 `localhost`)
|
||||
|
||||
### 第四步:SimpleWebCrawler 添加 SSRF 防护
|
||||
1. 新增 `_is_private_ip(ip)` 函数判断是否内网
|
||||
2. 爬取前:
|
||||
- 解析 URL 获取主机名
|
||||
- DNS 解析得到 IP 地址列表
|
||||
- 逐个检查,任一命中内网则拒绝
|
||||
- 检查协议,只允许 http/https
|
||||
3. 跟随重定向时重复检查
|
||||
|
||||
### 第五步:频率限制实现
|
||||
1. `WebSocketHandler` 维护 `rate_limit: Dict[connection_id, (last_time, count)]`
|
||||
2. 每次处理消息前检查计数,超限拒绝
|
||||
|
||||
### 第六步:update_identity/update_soul 路径加固
|
||||
1. 只允许写 `00_IDENTITY.md` 和 `99_SOUL.md`
|
||||
2. 文件名硬编码,不允许用户指定路径
|
||||
3. 写入前 `realpath` 校验,确保在 `prompts_dir` 内
|
||||
|
||||
### 第七步:其他细节
|
||||
1. 启动时强制校验 API Key,不存在立即退出
|
||||
2. 错误信息脱敏,服务端记录详细错误,客户端只返回通用错误
|
||||
3. 日志脱敏,不打印 API Key/token
|
||||
|
||||
## 配置文件变化(`config.json`)
|
||||
|
||||
```json
|
||||
{
|
||||
// ... 原有配置不变 ...
|
||||
"auth": {
|
||||
"token": "your-secret-token-here", // 新增 必填
|
||||
"enabled": true // 新增 默认 true
|
||||
},
|
||||
"rate_limit": {
|
||||
"enabled": true, // 新增 默认 true
|
||||
"max_requests_per_minute": 30 // 新增 默认 30
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 部署说明
|
||||
|
||||
开放版必须通过 gateway/WAF 反向代理接入:
|
||||
```
|
||||
Client → WAF (TLS 终止) → gateway (认证) → aibox (127.0.0.1:8765)
|
||||
```
|
||||
- aibox 只监听 `127.0.0.1`,不直接暴露
|
||||
- gateway 完成 token/JWT 认证
|
||||
- WAF 处理 TLS 和规则过滤
|
||||
|
||||
## 安全验证点
|
||||
|
||||
发布前必须验证:
|
||||
|
||||
- [ ] `run_command` 工具不存在,无法调用
|
||||
- [ ] `read_file`/`write_file`/`list_files` 不存在
|
||||
- [ ] `create_skill`/`install_skill` 不存在
|
||||
- [ ] 无 token 连接被拒绝
|
||||
- [ ] 错误 token 连接被拒绝
|
||||
- [ ] 正确 token 可连接
|
||||
- [ ] 内网 URL 爬取被拒绝(SSRF 防护)
|
||||
- [ ] 超过频率限制被拒绝
|
||||
- [ ] 启动时无 API Key 直接退出
|
||||
- [ ] `update_identity`/`update_soul` 只能写固定文件,无法遍历
|
||||
- [ ] 只有 `.md` 技能被加载
|
||||
|
||||
## 发布路线图
|
||||
|
||||
| 阶段 | 任务 | 状态 |
|
||||
|------|------|------|
|
||||
| 阶段 1 | 工具裁剪、SkillManager 修改 | ⏸ 待开始 |
|
||||
| 阶段 2 | WebSocket 认证实现 | ⏸ 待开始 |
|
||||
| 阶段 3 | SSRF 防护(SimpleWebCrawler) | ⏸ 待开始 |
|
||||
| 阶段 4 | 频率限制、启动校验、错误脱敏 | ⏸ 待开始 |
|
||||
| 阶段 5 | 路径加固(update_identity/update_soul) | ⏸ 待开始 |
|
||||
| 阶段 6 | 安全验证测试 | ⏸ 待开始 |
|
||||
| 阶段 7 | 更新 README、更新文档 | ⏸ 待开始 |
|
||||
|
||||
## 风险与约束
|
||||
|
||||
- 阉割后功能减少,但安全性大大提高,可公开部署使用
|
||||
- 仍保留 AI 对话、记忆、任务调度、子代理、扩展 Markdown 技能等核心特性
|
||||
- 用户仍可通过 AI 能力总结、分析、创作,只是无法直接操作服务器文件和执行命令
|
||||
- 若需要文件操作,建议通过 gateway 提供受限 API,不开放给 aibox 直接操作
|
||||
@@ -0,0 +1,92 @@
|
||||
# libth 安全审计报告
|
||||
|
||||
**审计日期**: 2026-08-04
|
||||
**审计范围**: `libth/inc/us/`(`Thread.hpp`、`Fsm.hpp`、`ITask.hpp`、`Cv_wait.hpp`)、`libth/src/Thread.cpp`
|
||||
**代码版本**: dev_qing 分支
|
||||
|
||||
---
|
||||
|
||||
## 修复进度
|
||||
|
||||
| 优先级 | 总数 | 已修复 | 部分修复 | 未修复 |
|
||||
|--------|------|--------|----------|--------|
|
||||
| P0 | 0 | 0 | 0 | 0 |
|
||||
| P1 | 1 | 0 | 0 | 1 |
|
||||
| P2 | 4 | 0 | 0 | 4 |
|
||||
| P3 | 2 | 0 | 0 | 2 |
|
||||
| **合计** | **7** | **0** | **0** | **7** |
|
||||
|
||||
> 说明:libth 为底层基础线程库,无对外网络攻击面,风险集中在**同步原语缺失超时导致的潜在永久阻塞(DoS)**与**内存/生命周期健壮性**。以下问题均为设计层面缺陷,源码中已有 FIXME/README 标注,尚未修复。
|
||||
|
||||
---
|
||||
|
||||
## 🟠 P1 — 高危问题
|
||||
|
||||
| # | 问题 | 状态 | 说明 |
|
||||
|---|------|------|------|
|
||||
| P1-1 | `Cv_wait` 无超时机制,可永久阻塞 | ❌ 未修复 | `Cv_wait.hpp` L21-25 `Wait()` 依赖 `cv.wait(lock, [this]{return flag;})`,无超时与超时中断。若 `WakeCv()` 永不触发(如等待的信号/事件丢失),调用线程将**永久阻塞**。被 `core` 的 `main.cpp` 直接使用,一旦误用即造成服务线程卡死(DoS)。源码 FIXME 已标注"仅支持单次等待-唤醒、可能导致永久堵塞,应改用 `std::future`/`std::promise`" |
|
||||
|
||||
---
|
||||
|
||||
## 🟡 P2 — 中危问题
|
||||
|
||||
| # | 问题 | 状态 | 说明 |
|
||||
|---|------|------|------|
|
||||
| P2-1 | `Thread::suspend()` 无超时 | ❌ 未修复 | `Thread.cpp` L33-35 透传 `Fsm::suspend()`(`Fsm.hpp` L61-65),阻塞等待状态变更却无超时。若其他线程永不调用 `set()`/`run()`/`stop()`/`shut()`,当前线程永久挂起,造成线程资源耗尽 |
|
||||
| P2-2 | 异常回退后未释放资源,可能内存泄漏 | ❌ 未修复 | `Thread.cpp` L132-135 `catch(std::exception)` 后仅 `stop()` 回退 STOP,不触发清理。若 `LoopEvent` 在循环中反复抛异常,会反复进入 STOP→START→RUNNING 循环,每次泄漏部分资源,长期运行内存持续增长。README 已注明"catch 后继续运行可能造成内存泄漏" |
|
||||
| P2-3 | `wake()`/`WaitStart()` 死锁约束脆弱 | ❌ 未修复 | `Thread.cpp` L87-92 `WaitStart()` 先持有 `mtx_th` 再调 `wake()`,而 `wake()`(L38-45)内部禁止再次加锁。该约束依赖调用方自觉遵守,一旦在 `WakeEvent` 回调中误加锁即死锁,缺少防御性校验(如 `std::recursive_mutex` 或断言) |
|
||||
| P2-4 | `WaitStart()` 存在潜在死锁/异常风险 | ❌ 未修复 | `Thread.hpp` L113 注释"据说该函数有问题可能导致异常"。`WaitStart()` 在 `wake()` 后置 `ready=false` 再 `cv.wait`,若 `WakeEvent` 中未调用 `run()`/`stop()`/`shut()`,`ready` 永不置位,调用线程永久阻塞 |
|
||||
|
||||
---
|
||||
|
||||
## 🔵 P3 — 低危问题
|
||||
|
||||
| # | 问题 | 状态 | 说明 |
|
||||
|---|------|------|------|
|
||||
| P3-1 | `Activate()` 重复调用静默返回 | ❌ 未修复 | `Thread.cpp` L80 若 `th` 已存在直接 `return`,不报错也不提示。调用方误以为重复激活有效,实际第二个线程未创建,状态不一致且无任何反馈 |
|
||||
| P3-2 | `Cv_wait` 复用后行为异常 | ❌ 未修复 | `Cv_wait.hpp` L19 `flag` 非原子且复用后残留 `true`,导致后续 `Wait()` 立即返回而不阻塞。README 已注明"仅支持单次使用,复用导致 `Wait()` 不阻塞" |
|
||||
|
||||
---
|
||||
|
||||
## ✅ 已有安全机制
|
||||
|
||||
1. ✅ **线程安全状态机**: `Fsm` 用 `std::mutex` 保护状态读写(`Fsm.hpp` L31-34、L50-57),避免并发设置状态的数据竞争
|
||||
2. ✅ **两阶段构造防竞态**: `Activate()` 与构造函数分离(`Thread.hpp` L110),避免 `std::thread` 在对象构造完成前访问未初始化成员
|
||||
3. ✅ **异常捕获防进程崩溃**: `main()` 循环捕获 `std::exception` 回退 STOP(`Thread.cpp` L108-135),单线程异常不会拖垮整个进程
|
||||
4. ✅ **RAII 资源管理**: 使用 `std::unique_ptr<std::thread>`、`std::unique_ptr<f_t>`(`Thread.hpp` L128-146),避免裸指针管理
|
||||
5. ✅ **复制构造禁用**: `Thread(const Thread&) = delete`(`Thread.hpp` L41),防止线程对象被误拷贝造成双重管理
|
||||
6. ✅ **回调组合优于继承**: 持 `std::function` 回调(`Thread.hpp` L24),接口收敛、避免子类化的状态泄漏
|
||||
|
||||
---
|
||||
|
||||
## 修复路线图
|
||||
|
||||
### 短期(72小时)
|
||||
- [ ] P1-1 `Cv_wait` 增加超时等待(`wait_for` + 超时返回),或按源码 FIXME 改用 `std::future`/`std::promise`
|
||||
- [ ] P2-1 `Fsm::suspend()`/`Thread::suspend()` 增加超时参数,避免永久挂起
|
||||
|
||||
### 中期
|
||||
- [ ] P2-2 异常回退时统一触发 `ClearEvent` 清理,避免长期运行内存泄漏
|
||||
- [ ] P2-3 将 `wake()` 的锁约束改为可防御形式(如 `std::recursive_mutex` 或加锁断言诊断)
|
||||
- [ ] P2-4 `WaitStart()` 增加超时与状态校验,避免 `WakeEvent` 未推进状态时永久阻塞
|
||||
|
||||
### 长期
|
||||
- [ ] P3-1 `Activate()` 重复调用改为抛异常或返回错误码,避免静默失败
|
||||
- [ ] P3-2 `Cv_wait` 的 `flag` 改为原子变量并支持复用,或明确废弃并替换
|
||||
|
||||
---
|
||||
|
||||
## 代码位置索引
|
||||
|
||||
| 功能 | 文件 | 行号 |
|
||||
|------|------|------|
|
||||
| `Cv_wait::Wait()` 无超时 | `inc/us/Cv_wait.hpp` | L21-25 |
|
||||
| `Cv_wait::flag` 非原子/复用残留 | `inc/us/Cv_wait.hpp` | L18-19 |
|
||||
| `Fsm::suspend()` 无超时 | `inc/us/Fsm.hpp` | L61-65 |
|
||||
| `Thread::suspend()` 透传 | `src/Thread.cpp` | L33-35 |
|
||||
| `Thread::WaitStart()` 唤醒等待 | `src/Thread.cpp` | L87-92 |
|
||||
| `Thread::wake()` 禁止加锁 | `src/Thread.cpp` | L38-45 |
|
||||
| `Thread::Activate()` 重复调用静默返回 | `src/Thread.cpp` | L76-85 |
|
||||
| `Thread::main()` 异常回退 | `src/Thread.cpp` | L103-138 |
|
||||
| `Thread` 复制构造禁用 | `inc/us/Thread.hpp` | L41 |
|
||||
| `Thread` 两阶段构造 | `inc/us/Thread.hpp` | L110 |
|
||||
Reference in new issue
Block a user