docs(wiki): Phase B+C完成 — middleware/saas/security/memory 5节模板重构

- middleware.md: 集成契约+3不变量+执行流 (157→136行)
- saas.md: 移除安全重复→引用security.md+Token Pool算法 (231→173行)
- security.md: 吸收saas认证内容成为安全唯一真相源 (158→199行)
- memory.md: 最大压缩363→147行+Hermes洞察提炼+4不变量

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
iven
2026-04-22 21:42:24 +08:00
parent 36612eac53
commit 5d88d129d1
5 changed files with 532 additions and 760 deletions

View File

@@ -1,6 +1,6 @@
---
title: 安全体系
updated: 2026-04-21
updated: 2026-04-22
status: active
tags: [module, security, auth, encryption]
---
@@ -9,37 +9,34 @@ tags: [module, security, auth, encryption]
> 从 [[index]] 导航。关联模块: [[saas]] [[routing]] [[middleware]]
## 设计思想
## 设计决策
**核心原则: 多层防御,深度安全。**
1. **认证层** — JWT + Cookie + TOTP 2FA + 账户锁定
2. **传输层** — CORS 白名单 + Cookie Secure + HTTPS (反向代理)
3. **存储层** — Argon2id 密码 + AES-256-GCM 加密 + OS Keyring
4. **运行时层** — 限流 + 配额 + CSP + Guardrail 中间件
| 决策 | 为什么 |
|------|--------|
| JWT + HttpOnly Cookie 双通道 | Tauri 桌面端用 OS keyring 存 JWT浏览器用 HttpOnly Cookie 防 XSS 窃取,双环境统一认证 |
| password_version (pwv) 失效 | 修改密码后自动使所有已签发 JWT 失效,无需 token 黑名单O(1) 验证 |
| TOTP AES-256-GCM 加密 | TOTP 共享密钥不能明文存储,随机 Nonce 防重放,生产环境强制独立密钥 |
| IP 级限流 + 持久化 | 防暴力破解login 5/min、防刷注册3/hour持久化到 PostgreSQL 避免重启丢失 |
| CORS 白名单强制 | 生产环境 `cors_origins` 缺失直接拒绝启动,不允许 `*` 通配 |
完整审计报告: `docs/features/SECURITY_PENETRATION_TEST_V1.md`
## 功能清单
## 关键文件 + 数据流
| 功能 | 描述 | 入口文件 | 状态 |
|------|------|----------|------|
| JWT 认证 | 签发/验证/刷新/失效 | auth/handlers.rs | ✅ |
| Cookie 双通道 | Tauri keyring + 浏览器 HttpOnly | auth/handlers.rs | ✅ |
| TOTP 2FA | 设置/验证/禁用 | auth/totp.rs | ✅ |
| 密码安全 | Argon2id + OsRng 盐 + pwv 失效 | auth/handlers.rs | ✅ |
| Token 池加密 | AES-256-GCM + 随机 Nonce | relay/key_pool.rs | ✅ |
| OS Keyring | Win DPAPI/macOS Keychain/Linux Secret | secure_storage.rs | ✅ |
| 本地记忆加密 | AES-256-GCM (可选) | memory/crypto.rs | ✅ |
| 账户锁定 | 5 次失败锁 15 分钟 | auth/handlers.rs | ✅ |
| 限流 | IP 级 + 账户级滑动窗口 | middleware.rs | ✅ |
| CORS 白名单 | 生产缺失拒绝启动 | main.rs CorsLayer | ✅ |
| CSP | Tauri 移除 unsafe-inline | desktop/src-tauri/ | ✅ |
| Admin 权限 | admin_guard + RBAC | middleware.rs | ✅ |
### 核心文件
## 代码逻辑
| 文件 | 职责 |
|------|------|
| `crates/zclaw-saas/src/auth/handlers.rs` | 认证端点: 登录/注册/刷新/TOTP/密码修改 |
| `crates/zclaw-saas/src/auth/totp.rs` | TOTP 2FA: QR 生成 + 验证 + AES-256-GCM 加密 |
| `crates/zclaw-saas/src/middleware.rs` | HTTP 中间件栈 (10 层): 认证/限流/配额/CORS |
| `crates/zclaw-saas/src/relay/key_pool.rs` | Token Pool: Key 加密存储 + RPM/TPM 轮换 |
| `desktop/src-tauri/src/secure_storage.rs` | OS Keyring: Win DPAPI / macOS Keychain / Linux Secret |
| `desktop/src-tauri/src/memory/crypto.rs` | 本地记忆加密: AES-256-GCM (可选) |
### 认证流
### 认证数据
```
用户登录 (POST /api/v1/auth/login)
@@ -57,50 +54,26 @@ tags: [module, security, auth, encryption]
→ localStorage: 仅 saasUrl + account 非敏感信息
```
### JWT Password Version 失效机制
### 集成契约
```
JWT Claims 含 pwv (password_version) 字段
→ 每次验证 JWT 时比对 Claims.pwv vs DB.pwv
→ 修改密码 → DB.pwv 递增 → 所有旧 JWT 自动失效
```
| 方向 | 接口 | 说明 |
|------|------|------|
| Provides --> saas | auth_middleware, JWT validation, rate limiting | 每个 API 请求经过认证层 |
| Provides --> desktop | secure_storage, crypto_utils | 配置/凭据安全存储 |
| Provides --> admin | admin_guard_middleware | Admin 路由权限验证 |
### Token 池安全
### Auth API 接口
```
Provider Key 存储: AES-256-GCM + 随机 Nonce 加密
→ 解密失败: warn + skip 到下一个 Key (不阻塞 relay)
→ 启动时: heal_provider_keys() 自动重新加密有效 Key
→ TOTP 加密密钥: 生产环境强制独立 ZCLAW_TOTP_ENCRYPTION_KEY
```
### SaaS HTTP 中间件栈 (10 层)
| # | 中间件 | 路由组 | 功能 |
|---|--------|--------|------|
| 1 | public_rate_limit | Public | IP 限流: login 5/min, register 3/hour |
| 2 | auth_middleware | Protected+Relay | JWT/Cookie/API Token 身份验证 |
| 3 | rate_limit_middleware | Protected+Relay | 账户级请求频率限制 |
| 4 | quota_check_middleware | Relay | 月度配额检查 (relay_requests + input_tokens) |
| 5 | request_id_middleware | All | UUID 请求追踪 |
| 6 | api_version_middleware | All | API 版本头 |
| 7 | TimeoutLayer (15s) | Protected | 非流式请求超时 |
| 8 | admin_guard | Admin 子路由 | admin 权限验证 |
| G1 | TraceLayer | All | HTTP 请求追踪 |
| G2 | CorsLayer | All | CORS 白名单 (生产缺失拒绝启动) |
## API 接口
### Auth 公开路由
**公开路由:**
| 方法 | 路径 | 说明 |
|------|------|------|
| POST | `/api/v1/auth/register` | 注册 (邮箱 RFC 5322 + 254 字符限制) |
| POST | `/api/v1/auth/register` | 注册 (邮箱 RFC 5322 + 254 字符) |
| POST | `/api/v1/auth/login` | 登录 (5 次/分钟 IP 限流) |
| POST | `/api/v1/auth/refresh` | 刷新 Token (单次使用旧 token 撤销到 DB) |
| POST | `/api/v1/auth/refresh` | Token 刷新 (单次使用, 旧 token 撤销) |
| POST | `/api/v1/auth/logout` | 登出 |
### Auth 受保护路由
**受保护路由:**
| 方法 | 路径 | 说明 |
|------|------|------|
@@ -110,7 +83,7 @@ Provider Key 存储: AES-256-GCM + 随机 Nonce 加密
| POST | `/api/v1/auth/totp/verify` | TOTP 验证激活 |
| POST | `/api/v1/auth/totp/disable` | TOTP 禁用 (需密码) |
### Tauri 安全命令
**Tauri 安全命令:**
| 命令 | 说明 |
|------|------|
@@ -119,39 +92,108 @@ Provider Key 存储: AES-256-GCM + 随机 Nonce 加密
| `secure_store_delete` | OS Keyring 删除 |
| `secure_store_is_available` | Keyring 可用性检测 |
## 测试链路
## 代码逻辑
| 功能 | 测试文件 | 覆盖状态 |
|------|---------|---------|
| 认证流程 | `crates/zclaw-saas/tests/auth_test.rs` | ✅ |
| 认证安全边界 | `crates/zclaw-saas/tests/auth_security_test.rs` | ✅ |
| 账户安全 | `crates/zclaw-saas/tests/account_security_test.rs` | ✅ |
| 权限矩阵 | `crates/zclaw-saas/tests/permission_matrix_test.rs` | ✅ |
| TOTP | `crates/zclaw-saas/src/auth/totp.rs` inline | ✅ |
| 本地加密 | `desktop/src-tauri/src/memory/crypto.rs` inline | ✅ |
### JWT Password Version 失效机制
## 关联模块
```
JWT Claims 含 pwv (password_version) 字段
→ auth_middleware 每次验证 JWT 时: Claims.pwv vs DB.pwv
→ 不匹配 → 401 Unauthorized
→ 修改密码 → DB.pwv 递增 → 所有旧 JWT 自动失效
→ 无需 token 黑名单,验证成本 O(1)
```
- [[saas]] — 安全体系运行在 SaaS 后端
- [[routing]] — SaaS JWT 用于 relay 认证
- [[middleware]] — Guardrail + LoopGuard + SubagentLimit 运行时安全
### 密码存储: Argon2id + OsRng
## 关键文件
```
注册/修改密码:
→ OsRng 生成随机盐
→ Argon2id 哈希 (内存硬 + 时间成本)
→ 存储到 users.password_hash
验证:
→ Argon2id::verify(password, stored_hash)
→ 失败计数递增 → 5 次后锁定 15 分钟
```
| 文件 | 职责 |
|------|------|
| `crates/zclaw-saas/src/auth/handlers.rs` | 认证端点实现 |
| `crates/zclaw-saas/src/auth/totp.rs` | TOTP 2FA 实现 |
| `crates/zclaw-saas/src/middleware.rs` | HTTP 中间件栈 |
| `crates/zclaw-saas/src/relay/key_pool.rs` | Token Pool + Key 加密 |
| `desktop/src-tauri/src/secure_storage.rs` | OS Keyring 接口 |
| `desktop/src-tauri/src/memory/crypto.rs` | 本地 AES-256-GCM |
| `docs/features/SECURITY_PENETRATION_TEST_V1.md` | 安全审计报告 |
### TOTP / API Key 加密: AES-256-GCM
## 已知问题
```
TOTP 密钥存储:
→ 随机生成 12 字节 Nonce
→ AES-256-GCM 加密 (密钥: ZCLAW_TOTP_ENCRYPTION_KEY, 64 hex)
→ 存储 nonce + ciphertext
解密:
→ 取出 nonce → AES-256-GCM 解密
→ 解密失败: warn + 跳过 (不阻塞认证)
-**JWT 签名密钥 fallback**`#[cfg(debug_assertions)]` 保护release 拒绝启动
-**TOTP 加密密钥解耦** — 生产环境强制独立 `ZCLAW_TOTP_ENCRYPTION_KEY`
-**Cookie Secure 标志** — dev=false, prod=true
-**CORS 白名单** — 生产缺失拒绝启动
-**Admin 404→403** — admin_guard_middleware 已修复
Provider API Key 同理: heal_provider_keys() 启动时重新加密有效 Key
```
### Token 刷新轮换
```
POST /api/v1/auth/refresh
→ 验证 refresh_token 有效性
→ 检查旧 token 是否已撤销 (rotation 防重放)
→ 撤销旧 refresh_token (写入 DB revoked_at)
→ 签发新 access_token (2h) + refresh_token (7d)
```
### 限流规则
| 端点 | 限制 | 持久化 |
|------|------|--------|
| `/api/auth/login` | 5 次/分钟/IP | PostgreSQL |
| `/api/auth/register` | 3 次/小时/IP | PostgreSQL |
| 公共端点 | 20 次/分钟/IP | 内存 |
### SaaS HTTP 中间件栈 (10 层)
| # | 中间件 | 路由组 | 功能 |
|---|--------|--------|------|
| 1 | public_rate_limit | Public | IP 限流 |
| 2 | auth_middleware | Protected+Relay | JWT/Cookie/API Token 身份验证 |
| 3 | rate_limit_middleware | Protected+Relay | 账户级频率限制 |
| 4 | quota_check_middleware | Relay | 月度配额检查 |
| 5 | request_id_middleware | All | UUID 请求追踪 |
| 6 | api_version_middleware | All | API 版本头 |
| 7 | TimeoutLayer (15s) | Protected | 非流式请求超时 |
| 8 | admin_guard | Admin 子路由 | admin 权限验证 |
| G1 | TraceLayer | All | HTTP 请求追踪 |
| G2 | CorsLayer | All | CORS 白名单 |
## 活跃问题 + 陷阱
| 问题 | 级别 | 说明 |
|------|------|------|
| CSP 已加固 | Done | Tauri 移除 `unsafe-inline` script`connect-src` 限制 `http://localhost:*` + `https://*` |
| TLS 依赖反向代理 | 长期 | Axum 不负责 TLSnginx/caddy 提供 HTTPS 终止 |
| Cookie Secure 开发环境 false | 设计意图 | 开发环境 HTTP 无 Secure生产必须 true |
陷阱:
- JWT 签名密钥: `#[cfg(debug_assertions)]` 有 fallbackrelease 模式直接 `bail` 拒绝启动
- TOTP 加密密钥: 生产必须独立设置 `ZCLAW_TOTP_ENCRYPTION_KEY`,不从 JWT 密钥派生
- CORS 白名单: 生产缺失拒绝启动,不允许通配符
- Refresh Token: 单次使用logout 时撤销到 DBrotation 校验已撤销的旧 token
## 变更日志
| 日期 | 变更 | 提交 |
|------|------|------|
| 2026-04-21 | 移除数据脱敏中间件 (稳定化约束) | fa5ab4e |
| 2026-04-17 | E2E 测试安全链路验证通过 | — |
| 2026-04-16 | Agent 隔离修复 + Admin 权限校验 | — |
| 2026-04-13 | 安全渗透测试 V1: 15 项修复 | — |
| 2026-04-09 | CSP 加固 + JWT pwv + 账户锁定 + TOTP 解耦 | — |
### 测试覆盖
| 功能 | 测试文件 |
|------|---------|
| 认证流程 | `crates/zclaw-saas/tests/auth_test.rs` |
| 认证安全边界 | `crates/zclaw-saas/tests/auth_security_test.rs` |
| 账户安全 | `crates/zclaw-saas/tests/account_security_test.rs` |
| 权限矩阵 | `crates/zclaw-saas/tests/permission_matrix_test.rs` |
| TOTP | `crates/zclaw-saas/src/auth/totp.rs` inline tests |
| 本地加密 | `desktop/src-tauri/src/memory/crypto.rs` inline tests |