feat(protocol): 添加补丁管理和行为指标协议类型 feat(client): 实现补丁管理插件采集功能 feat(server): 添加补丁管理和异常检测API feat(database): 新增补丁状态和异常检测相关表 feat(web): 添加补丁管理和异常检测前端页面 fix(security): 增强输入验证和防注入保护 refactor(auth): 重构认证检查逻辑 perf(service): 优化Windows服务恢复策略 style: 统一健康评分显示样式 docs: 更新知识库文档
256 lines
11 KiB
Markdown
256 lines
11 KiB
Markdown
# CSM 安全审计报告
|
||
|
||
> **审计日期**: 2026-04-11 | **审计范围**: 全系统 (Server + Client + Protocol + Frontend)
|
||
> **方法论**: OWASP Top 10 (2021), CWE Top 25, 手动源码审查 + 攻击者视角分析
|
||
|
||
---
|
||
|
||
## 执行摘要
|
||
|
||
| 严重级别 | 数量 | 说明 |
|
||
|----------|------|------|
|
||
| **CRITICAL** | 4 | 可直接被远程利用,导致系统完全沦陷 |
|
||
| **HIGH** | 12 | 需特定条件但影响重大 |
|
||
| **MEDIUM** | 12 | 有限影响或需较高权限 |
|
||
| **LOW** | 8 | 纵深防御建议 |
|
||
|
||
**最关键发现**: JWT Secret 硬编码在版本控制中、注册 Token 为空允许任意设备注册、凭据文件无 ACL 保护、默认无 TLS 传输加密。这四个问题组合意味着攻击者可以在数分钟内完全接管系统。
|
||
|
||
---
|
||
|
||
## CRITICAL (4)
|
||
|
||
### AUD-001: JWT Secret 硬编码在 config.toml 中
|
||
|
||
- **文件**: `config.toml:12` → `crates/server/src/config.rs`
|
||
- **CWE**: CWE-798 (硬编码凭证)
|
||
- **OWASP**: A07:2021 - 安全配置错误
|
||
|
||
**漏洞代码**:
|
||
```toml
|
||
jwt_secret = "39ffc129-dd62-4eb4-bbc0-8bf4b8e2ccc7"
|
||
```
|
||
|
||
**攻击场景**: 任何能访问仓库的人可提取 secret,为任意用户(含 admin)伪造 JWT,获得系统完全管理控制权,可推送恶意配置到所有医院终端、禁用安全控制、篡改审计记录。
|
||
|
||
**修复**:
|
||
1. 从 `config.toml` 中移除硬编码 secret
|
||
2. 通过 `CSM_JWT_SECRET` 环境变量独占加载
|
||
3. secret 为空时拒绝启动
|
||
4. **立即轮换**已泄露的 secret `39ffc129-dd62-4eb4-bbc0-8bf4b8e2ccc7`
|
||
5. 将 `config.toml` 加入 `.gitignore`
|
||
|
||
---
|
||
|
||
### AUD-002: 空 Registration Token — 任意设备注册
|
||
|
||
- **文件**: `config.toml:1`, `crates/server/src/tcp.rs:549-558`
|
||
- **CWE**: CWE-306 (关键功能缺失认证)
|
||
- **OWASP**: A07:2021
|
||
|
||
**漏洞代码**:
|
||
```toml
|
||
registration_token = ""
|
||
```
|
||
```rust
|
||
if !expected_token.is_empty() { // 空字符串直接跳过验证
|
||
```
|
||
|
||
**攻击场景**: TCP 端口 9999 可达的任何攻击者可注册恶意设备,注入伪造审计数据掩盖安全事件,获取所有插件配置(含安全策略)。
|
||
|
||
**修复**: 设置强 registration token,空 token 时拒绝启动。
|
||
|
||
---
|
||
|
||
### AUD-003: Windows 凭据文件无 ACL 保护
|
||
|
||
- **文件**: `crates/client/src/main.rs:280-283`
|
||
- **CWE**: CWE-732 (关键资源权限不当)
|
||
- **OWASP**: A01:2021
|
||
|
||
**漏洞代码**:
|
||
```rust
|
||
#[cfg(not(unix))]
|
||
fn write_restricted_file(path: &std::path::Path, content: &str) -> std::io::Result<()> {
|
||
std::fs::write(path, content) // 无任何 ACL 设置
|
||
}
|
||
```
|
||
|
||
**攻击场景**: `device_secret.txt`(HMAC 密钥)和 `device_uid.txt`(设备身份)以默认权限写入,设备上任何用户进程可读取。攻击者可提取密钥伪造心跳,在不同机器上模拟设备。
|
||
|
||
**修复**: 使用 `icacls` 或 `SetSecurityInfo` 设置仅 SYSTEM 可访问的 ACL。
|
||
|
||
---
|
||
|
||
### AUD-004: 默认无 TLS — 明文传输所有敏感数据
|
||
|
||
- **文件**: `config.toml` (无 `[server.tls]`), `crates/server/src/tcp.rs:411-414`
|
||
- **CWE**: CWE-319 (明文传输敏感信息)
|
||
- **OWASP**: A02:2021
|
||
|
||
**攻击场景**: TCP 端口 9999 以明文运行。`device_secret`、所有插件配置、Web 过滤规则、USB 策略、软件黑名单均以明文 JSON 传输。网络嗅探者可提取设备认证密钥并逆向工程安全策略。
|
||
|
||
**修复**: 生产环境强制 TLS,无 TLS 时拒绝启动(`CSM_DEV=1` 除外)。
|
||
|
||
---
|
||
|
||
## HIGH (12)
|
||
|
||
### AUD-005: Refresh Token 未存储 — 撤销机制不完整
|
||
|
||
- **文件**: `crates/server/src/api/auth.rs:131-133`
|
||
- **CWE**: CWE-613
|
||
- `refresh_tokens` 表存在但从未写入。登录不存储 token record,刷新仅检查 family 是否撤销,不验证 token 是否曾被实际颁发。无法强制注销所有 session。
|
||
|
||
### AUD-006: Refresh Token Family Rotation 存在 TOCTOU 竞争条件
|
||
|
||
- **文件**: `crates/server/src/api/auth.rs:167-183`
|
||
- **CWE**: CWE-367
|
||
- 检查 family 撤销状态与执行撤销之间无事务保护。并发使用同一 stolen token 的两个请求均可通过检查,攻击者获得全新的未撤销 token family。
|
||
|
||
### AUD-007: 客户端不验证服务器身份
|
||
|
||
- **文件**: `crates/client/src/network/mod.rs:149-162`
|
||
- **CWE**: CWE-295
|
||
- 客户端盲目连接任何响应的服务器。ARP/DNS 欺骗攻击者可推送恶意配置(禁用所有安全插件、注入有害规则)。HMAC 仅保护心跳,不保护配置推送。
|
||
|
||
### AUD-008: PowerShell 命令注入面
|
||
|
||
- **文件**: `crates/client/src/asset/mod.rs:82-83`, `crates/client/src/clipboard_control/mod.rs:143-159`
|
||
- **CWE**: CWE-78
|
||
- `powershell_lines()` 通过 `format!()` 拼接命令参数。若服务器推送含引号/转义字符的恶意规则,可能导致 PowerShell 命令注入。
|
||
|
||
### AUD-009: 服务停止/卸载未受保护
|
||
|
||
- **文件**: `crates/client/src/service.rs:17-69`
|
||
- **CWE**: CWE-284
|
||
- `csm-client.exe --uninstall` 无认证保护。终端管理员权限用户可完全移除安全代理,绕过所有监控。无服务恢复策略、无看门狗进程、无反调试保护。
|
||
|
||
### AUD-010: JWT Token 存储在 localStorage
|
||
|
||
- **文件**: `web/src/lib/api.ts:25-28, 175-176`
|
||
- **CWE**: CWE-922
|
||
- Access token 和 refresh token 均存储在 `localStorage`,可被同源任意 JS 访问。XSS 漏洞可直接窃取 7 天有效期的 refresh token。
|
||
|
||
### AUD-011: CSP 允许 unsafe-inline + unsafe-eval
|
||
|
||
- **文件**: `crates/server/src/main.rs:142-143`
|
||
- **CWE**: CWE-693
|
||
- `script-src 'self' 'unsafe-inline' 'unsafe-eval'` 使 CSP 对 XSS 几乎无效。结合 localStorage token 存储,单个 XSS 即可导致管理员会话完全沦陷。
|
||
|
||
### AUD-012: WebSocket JWT 在 URL 查询参数中
|
||
|
||
- **文件**: `crates/server/src/ws.rs:36-73`
|
||
- **CWE**: CWE-312
|
||
- JWT 通过 `/ws?token=eyJ...` 传输。Token 出现在浏览器历史、服务器访问日志、代理日志中。且 WebSocket handler 不检查用户角色,非管理员可接收所有广播事件。
|
||
|
||
### AUD-013: 告警规则 Webhook SSRF
|
||
|
||
- **文件**: `crates/server/src/api/alerts.rs:115-131`
|
||
- **CWE**: CWE-918
|
||
- `notify_webhook` 字段无 URL 验证。可设置为 `http://169.254.169.254/latest/meta-data/` (AWS 元数据) 或 `file:///etc/passwd`,将服务器变成 SSRF 代理。
|
||
|
||
### AUD-014: 仅基于用户名的速率限制可绕过
|
||
|
||
- **文件**: `crates/server/src/api/auth.rs:101`
|
||
- **CWE**: CWE-307
|
||
- 速率限制仅以用户名为 key。攻击者可用 `Admin`、`ADMIN` 等变体绕过。无 IP 限制,可分布式暴力破解。
|
||
|
||
### AUD-015: 磁盘加密确认在只读路由层 — 权限提升
|
||
|
||
- **文件**: `crates/server/src/api/plugins/mod.rs:42`
|
||
- **CWE**: CWE-862
|
||
- PUT `acknowledge_alert` 在 `read_routes()` 中(仅需认证,不需 admin)。任何认证用户可确认(忽略)加密告警,掩盖合规违规。
|
||
|
||
### AUD-016: 初始管理员密码输出到 stderr
|
||
|
||
- **文件**: `crates/server/src/main.rs:270-275`
|
||
- **CWE**: CWE-532
|
||
- 初始密码通过 `eprintln!` 输出。容器化部署中 stderr 被日志聚合系统捕获,有日志访问权限者可获取管理员密码。
|
||
|
||
---
|
||
|
||
## MEDIUM (12)
|
||
|
||
| # | 发现 | 文件 | CWE |
|
||
|---|------|------|-----|
|
||
| AUD-017 | 多个 Update handler 跳过输入验证 (软件黑名单/Web过滤器/剪贴板) | `software_blocker.rs:79`, `web_filter.rs:62`, `clipboard_control.rs:107` | CWE-20 |
|
||
| AUD-018 | USB 策略 rules 字段接受任意 JSON 无验证 | `usb.rs:94-135` | CWE-20 |
|
||
| AUD-019 | 密码无最大长度限制 (bcrypt 72 字节截断) | `auth.rs:303` | CWE-20 |
|
||
| AUD-020 | 多个字段缺少长度验证 (弹出窗口/剪贴板/USB策略名) | 多处 | CWE-20 |
|
||
| AUD-021 | 多个列表端点无分页 (黑名单/白名单/规则/策略) | 多处 | CWE-770 |
|
||
| AUD-022 | 磁盘加密状态列表无分页可全库转储 | `disk_encryption.rs:12-55` | CWE-770 |
|
||
| AUD-023 | JWT 角色仅信任 claim 不查库 (降级延迟) | `auth.rs:273` | CWE-863 |
|
||
| AUD-024 | 缺少 HSTS 头 | `main.rs:123-143` | CWE-319 |
|
||
| AUD-025 | CORS 配置需严格限制 | `main.rs:284-301` | CWE-942 |
|
||
| AUD-026 | 日志中泄露设备 UID 和服务器地址 | `main.rs:62`, `network/mod.rs:34` | CWE-532 |
|
||
| AUD-027 | 注册 Token 回退空字符串 | `main.rs:72` | CWE-254 |
|
||
| AUD-028 | conflict.rs 中 format! SQL 模式 (当前安全但脆弱) | `conflict.rs:205` | CWE-89 |
|
||
|
||
---
|
||
|
||
## LOW (8)
|
||
|
||
| # | 发现 | 文件 |
|
||
|---|------|------|
|
||
| AUD-029 | 组名未过滤 HTML 特殊字符 | `groups.rs:72-96` |
|
||
| AUD-030 | 弹出窗口阻止器更新可创建无过滤器的规则 | `popup_blocker.rs:67-104` |
|
||
| AUD-031 | 设备删除非原子 (自毁帧在事务前发送) | `devices.rs:215-306` |
|
||
| AUD-032 | 受保护进程列表硬编码且可修补绕过 | `software_blocker/mod.rs:9-73` |
|
||
| AUD-033 | hosts 文件修改可能与 EDR 冲突 | `web_filter/mod.rs:59-93` |
|
||
| AUD-034 | 软件拦截器 TOCTOU 竞争条件 (已缓解) | `software_blocker/mod.rs:329-386` |
|
||
| AUD-035 | 前端路由守卫不验证 JWT 签名 | `router/index.ts:38-49` |
|
||
| AUD-036 | WebSocket 不验证入站消息 (当前丢弃) | `ws.rs:108-119` |
|
||
|
||
---
|
||
|
||
## 修复优先级
|
||
|
||
### P0 — 立即 (24h)
|
||
|
||
| 修复项 | 对应发现 | 工作量 |
|
||
|--------|---------|--------|
|
||
| 轮换 JWT Secret,移至环境变量 | AUD-001 | 30min |
|
||
| 设置非空 registration_token | AUD-002 | 15min |
|
||
| 凭据文件添加 Windows ACL | AUD-003 | 1h |
|
||
| 生产环境强制 TLS | AUD-004 | 2h |
|
||
|
||
### P1 — 短期 (1 周)
|
||
|
||
| 修复项 | 对应发现 | 工作量 |
|
||
|--------|---------|--------|
|
||
| Refresh token 存储到 DB + 事务保护 | AUD-005, 006 | 4h |
|
||
| Update handler 添加输入验证 | AUD-017 | 4h |
|
||
| Webhook URL 验证防 SSRF | AUD-013 | 1h |
|
||
| 磁盘加密确认移至 admin 路由 | AUD-015 | 15min |
|
||
| 初始密码写入文件替代 stderr | AUD-016 | 30min |
|
||
| 添加 IP 速率限制 | AUD-014 | 2h |
|
||
|
||
### P2 — 中期 (1 月)
|
||
|
||
| 修复项 | 对应发现 | 工作量 |
|
||
|--------|---------|--------|
|
||
| Token 迁移至 HttpOnly Cookie | AUD-010 | 8h |
|
||
| CSP 强化 (nonce-based) | AUD-011 | 4h |
|
||
| WebSocket ticket 认证 | AUD-012 | 4h |
|
||
| 服务器身份验证 (证书固定) | AUD-007 | 8h |
|
||
| 服务保护 (恢复策略/看门狗) | AUD-009 | 4h |
|
||
| PowerShell 注入面消除 | AUD-008 | 6h |
|
||
| HSTS + Permissions-Policy 头 | AUD-024, 036 | 1h |
|
||
| 分页补充 | AUD-021, 022 | 4h |
|
||
|
||
---
|
||
|
||
## 安全亮点 (做得好的地方)
|
||
|
||
1. **SQL 注入防御**: 全库一致使用 `sqlx::bind()` 参数化
|
||
2. **错误处理**: `ApiResponse::internal_error()` 不泄露内部错误详情
|
||
3. **密码哈希**: bcrypt cost=12,符合行业标准
|
||
4. **Token Family 轮换**: 检测 token 重放并撤销整个 family
|
||
5. **常量时间比较**: 注册 token 验证已使用 `constant_time_eq()`
|
||
6. **帧速率限制**: 100 帧/5秒/连接
|
||
7. **审计日志**: 所有管理员写入操作记录到 `admin_audit_log`
|
||
8. **HMAC 心跳**: 设备认证使用 HMAC-SHA256
|
||
9. **进程保护列表**: 防止误杀系统关键进程
|
||
10. **输入验证**: Create handler 普遍有字段验证
|