- T40 UI 审计计划和结果文档(docs/qa/) - wiki 更新:miniprogram 设计系统合规审计记录 + index 关键数字更新 - 审计 V2 完整报告(docs/audits/v2/) - 讨论记录文档(docs/discussions/) - 设计规格和实施计划(docs/superpowers/) - 角色测试计划和结果(docs/qa/role-test-*) - Docker 生产部署配置
128 lines
4.9 KiB
Markdown
128 lines
4.9 KiB
Markdown
# V2 审计 — 安全合规与性能审计
|
||
|
||
> 日期: 2026-05-04
|
||
|
||
## 一、安全审计
|
||
|
||
### S1: SQL 注入防护 — 存在高危漏洞
|
||
|
||
| 编号 | 严重度 | 问题 | 位置 |
|
||
|------|--------|------|------|
|
||
| **SEC-01** | **高** | `patient_id`/`user_id` 通过 `format!` 拼接进 SQL | `action_inbox_service.rs:272-306` |
|
||
| SEC-01a | 低 | 迁移种子 SQL 使用 `format!` | 迁移文件(硬编码 UUID,风险极低) |
|
||
| -- | 安全 | `erp-plugin` 动态表操作 | `sanitize_identifier()` 40+ 处调用,覆盖完整 |
|
||
|
||
**SEC-01 详情**:
|
||
```rust
|
||
// action_inbox_service.rs 第 272-306 行
|
||
let patient_filter = match &query.patient_id {
|
||
Some(pid) => format!("AND patient_id = '{}'", pid), // 直接拼接!
|
||
None => String::new(),
|
||
};
|
||
let assigned_filter = format!("AND f.assigned_to = '{}'", user_id.unwrap()); // 直接拼接!
|
||
```
|
||
|
||
`tenant_id` 已正确使用 `$1` 参数化,但 `patient_id` 和 `user_id` 未参数化。虽然来源是已认证用户的上下文(非直接用户输入),但仍违反安全规范。
|
||
|
||
**修复**: 改为 `Statement::from_sql_and_values` 参数化绑定。
|
||
|
||
### S2: PII 加密覆盖 — 合规
|
||
|
||
AES-256-GCM + 随机 nonce + v1 前缀 + Zeroizing 密钥。
|
||
|
||
| Entity | 加密字段 |
|
||
|--------|---------|
|
||
| patient | id_number, phone, address, emergency_contact, emergency_phone |
|
||
| family_member | phone |
|
||
| doctor_profile | license_number |
|
||
| consultation_message | content |
|
||
| follow_up_record | result, plan, medication_notes |
|
||
| diagnosis | note, value |
|
||
| lab_report | report_content, conclusion |
|
||
| medication_record | note, value |
|
||
|
||
**结论**: 所有 PII 字段均已加密。解密使用 `unwrap_or` 降级兼容旧数据。
|
||
|
||
### S3: API Key 安全 (BLE 网关) — 合格
|
||
|
||
- 存储: SHA-256 哈希 + 前 8 位前缀双因子
|
||
- 生成: `OsRng` 32 字节随机密钥
|
||
- 传输: `Authorization: Gateway <key>` 或 `X-Gateway-Key` 头
|
||
- **不足**: 代码层未强制 HTTPS,需在反向代理层配置 TLS
|
||
|
||
### S4: OAuth 安全 — 部分合规
|
||
|
||
| 检查项 | 状态 |
|
||
|--------|------|
|
||
| scope 验证 | 合规 — 白名单校验 |
|
||
| client_secret 存储 | 合规 — Argon2 哈希 |
|
||
| 速率限制 | 已建模,未强制执行 |
|
||
| PKCE | 不支持(仅 Client Credentials) |
|
||
| redirect_uri | 不适用 |
|
||
|
||
### S5: 多租户隔离 — 合规(有一处增强建议)
|
||
|
||
- 应用层: 所有 handler 带 `ctx.tenant_id` 过滤
|
||
- 数据库层: PostgreSQL RLS 在所有含 tenant_id 表上启用
|
||
- SSE: 验证 `event.tenant_id != tenant_id` 跳过非本租户事件
|
||
- **不足**: FHIR `allowed_patient_ids` 在 JWT claims 中携带但未在查询层强制执行
|
||
|
||
### S6: 权限码一致性 — 存在缺失
|
||
|
||
| 编号 | 严重度 | 问题 |
|
||
|------|--------|------|
|
||
| **SEC-02** | **中** | OAuth 管理 handler(5 个端点)未调用 `require_permission()` |
|
||
|
||
所有其他 53 个 PermissionDescriptor 声明的权限码均有对应 `require_permission` 调用。
|
||
|
||
### S7: XSS 防护 — 合规
|
||
|
||
Web 和 MP 中均未发现 `dangerouslySetInnerHTML` 或 `innerHTML` 使用。
|
||
|
||
### S8: SSE 认证 — 合规
|
||
|
||
- AI SSE: JSON POST + `TenantContext` + `require_permission`
|
||
- 消息 SSE: JWT `?token=xxx` query param 回退 + tenant_id 验证
|
||
|
||
## 二、性能审计
|
||
|
||
### P1: N+1 查询 — 低风险
|
||
|
||
未发现典型 N+1 模式。告警引擎 `for rule in rules` 循环中有额外 DB 查询,但规则数量通常个位数。
|
||
|
||
### P2: 分页覆盖率 — 高
|
||
|
||
- 使用 `PaginationParams` 的 handler: 11 个文件
|
||
- 自定义分页: alert, action_inbox, patient
|
||
- 无分页: doctors, devices, tags, categories, consents, rules, templates, thresholds(通常数据量小)
|
||
|
||
### P3: AI 缓存 — 未启用
|
||
|
||
`AnalysisService::find_cached()` 方法存在但仅用于测试。生产流程未调用。`ai_usage.is_cache_hit` 字段已预留。
|
||
|
||
| 编号 | 严重度 | 问题 |
|
||
|------|--------|------|
|
||
| **PERF-01** | **中** | AI 分析缓存功能存在但未启用 |
|
||
|
||
### P4: 索引覆盖率 — 合规
|
||
|
||
最近 19 个迁移新增 22 个索引,覆盖所有新表。所有新表均含 `tenant_id` 索引。
|
||
|
||
### P5: 批量操作 — 高效
|
||
|
||
- 设备数据: `insert_many` + `ON CONFLICT DO NOTHING`
|
||
- 随访: `batch_create/assign/complete`
|
||
- 插件: `batch_delete/update` 使用 `IN (...)`
|
||
|
||
## 三、问题汇总
|
||
|
||
| 编号 | 严重度 | 类型 | 问题 | 位置 |
|
||
|------|--------|------|------|------|
|
||
| SEC-01 | **高** | 注入 | SQL `format!` 拼接 patient_id/user_id | action_inbox_service.rs:272-306 |
|
||
| SEC-02 | **中** | 权限 | OAuth handler 缺少 require_permission | oauth/handler.rs (5 端点) |
|
||
| SEC-03 | **中** | 越权 | FHIR allowed_patient_ids 未在查询层执行 | fhir/handler.rs |
|
||
| SEC-04 | **低** | 配置 | JWT secret dev fallback 硬编码 | oauth/middleware.rs:67 |
|
||
| SEC-05 | **低** | 限流 | rate_limit_per_minute 已建模未执行 | oauth/service.rs |
|
||
| PERF-01 | **中** | 缓存 | AI 分析缓存未启用 | erp-ai/service/analysis.rs |
|
||
| PERF-02 | **低** | 分页 | 部分列表端点缺分页 | 各 handler |
|