# 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 ` 或 `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 |