feat(protocol): 添加补丁管理和行为指标协议类型 feat(client): 实现补丁管理插件采集功能 feat(server): 添加补丁管理和异常检测API feat(database): 新增补丁状态和异常检测相关表 feat(web): 添加补丁管理和异常检测前端页面 fix(security): 增强输入验证和防注入保护 refactor(auth): 重构认证检查逻辑 perf(service): 优化Windows服务恢复策略 style: 统一健康评分显示样式 docs: 更新知识库文档
11 KiB
11 KiB
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 - 安全配置错误
漏洞代码:
jwt_secret = "39ffc129-dd62-4eb4-bbc0-8bf4b8e2ccc7"
攻击场景: 任何能访问仓库的人可提取 secret,为任意用户(含 admin)伪造 JWT,获得系统完全管理控制权,可推送恶意配置到所有医院终端、禁用安全控制、篡改审计记录。
修复:
- 从
config.toml中移除硬编码 secret - 通过
CSM_JWT_SECRET环境变量独占加载 - secret 为空时拒绝启动
- 立即轮换已泄露的 secret
39ffc129-dd62-4eb4-bbc0-8bf4b8e2ccc7 - 将
config.toml加入.gitignore
AUD-002: 空 Registration Token — 任意设备注册
- 文件:
config.toml:1,crates/server/src/tcp.rs:549-558 - CWE: CWE-306 (关键功能缺失认证)
- OWASP: A07:2021
漏洞代码:
registration_token = ""
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
漏洞代码:
#[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 |
安全亮点 (做得好的地方)
- SQL 注入防御: 全库一致使用
sqlx::bind()参数化 - 错误处理:
ApiResponse::internal_error()不泄露内部错误详情 - 密码哈希: bcrypt cost=12,符合行业标准
- Token Family 轮换: 检测 token 重放并撤销整个 family
- 常量时间比较: 注册 token 验证已使用
constant_time_eq() - 帧速率限制: 100 帧/5秒/连接
- 审计日志: 所有管理员写入操作记录到
admin_audit_log - HMAC 心跳: 设备认证使用 HMAC-SHA256
- 进程保护列表: 防止误杀系统关键进程
- 输入验证: Create handler 普遍有字段验证