Files
hms/docs/discussions/2026-06-25-analysis/05-compliance.md
iven 3351c68d10 docs: redact Redis 凭据明文 + 系统分析报告 + wiki 关键数字校正
PP-03 凭据泄露处置:
- 清除 wiki + 2 份历史文档中的 Redis 明文密码与公网 IP(4 文件 5 处)
- wiki 新增安全告警 + 症状导航条目
- 核实降级:泄露旧密码已失效,HMS 连本地 Redis,云端闲置;公网已关闭

系统深度分析(9 维度 + 6 主题多专家组):
- docs/discussions/2026-06-25-analysis/ 新增 7 文件
- 综合 6.8/10,4 CRITICAL,TOP 12 痛点,4 阶段路线图

wiki 关键数字校正(PP-02/05a fix 触发):
- 迁移数 175→176(m20260626_000170)
- 症状导航新增 device_readings 分区硬截止 + claim_next 注入修复条目
2026-06-26 09:07:35 +08:00

126 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 医疗合规与数据治理 — 主题综合
> 主持主题: 满足等保 2.0 / 数据安全法 / 个人信息保护法 / 病历管理规定,具备过认证能力,建立患者数据主权与可审计闭环。
> 日期: 2026-06-25 | 视角: 医疗合规专家 + 数据安全专家 + 隐私保护官(DPO) 三方综合
## 1. 主题愿景
把 HMS 从「修漏洞式合规」升级为「可举证的持续合规闭环」: 以**数据分类分级为底座**、**患者数据主权为核心**、**外部可验证的完整性举证为抗辩武器**, 让系统在等保三级测评、个保法年度审计、外资/跨境医疗租户采购评估三类场景下, 都能输出机器可验证的合规证据链, 而非依赖文档承诺或人工 review。
三位专家的核心共识: 当前 PII 加密(KEK+DEK+盲索引)、consent 实体、审计哈希链是难得的好底子, 但存在三类「结构性断裂」——加密不对称(name 明文漏网)、自动化只写不验(verify_hash_chain 无调用方 / DEK 版本硬编码为 1)、无数据主体权利履行通道(导出/删除/留存零实现)。这些不是上线前就绪度问题, 而是 6-12 个月合规演进必须补齐的能力。
## 2. 代码核实的关键事实(alreadyKnown 标注)
| # | 事实 | 证据 | 历史 | 增量判定 |
|---|------|------|------|----------|
| F1 | patient.name 明文存储 | `entity/patient.rs:12` `pub name: String`; `service/patient_service/crud.rs:174` `name: Set(req.name)`(同函数 id_number:178 走 pii::encrypt) | 00-INDEX PP-12 列了「name 加密 + name_hash」清单项 | 已识别但未深挖(对称加密缺失) |
| F2 | consent 医护角色全跳过 | `handler/consent_check.rs:9` BYPASS_ROLES=[admin,doctor,nurse,health_manager]; `:20` 命中即 return Ok | 无任何历史讨论提及 | **新发现** |
| F3 | verify_hash_chain 零调用 | `audit_service.rs:168` 定义, 全仓 grep 无调用方 | 无(PP-01/PP-05 同构「实现完整接线缺失」) | **新发现** |
| F4 | DEK key_version 硬编码 Some(1) | 7 处 `key_version: Set(Some(1))` 散布 patient/lab_report/diagnosis/doctor/follow_up/consultation service | 无 | **新发现**(轮换管线存在但消费侧硬编码) |
| F5 | erp-ai + erp-core 的 dev_default 无 cfg 防护 | `erp-core/src/crypto/mod.rs:37``erp-health/src/crypto.rs:48` 均无 `#[cfg(any(debug_assertions, test))]`; erp-ai 的 `config_resolver.rs:317` + `config_handler.rs:80` 直接调 `dev_default().kek()` | wiki 称「P4 dev_default 添加 cfg(debug_assertions)」仅 health/server 侧, erp-core/erp-ai 漏网 | **新发现**(推翻 wiki「已修复」) |
| F6 | consent_method 无签名验证 | `entity/consent.rs:21` `pub consent_method: Option<String>` 自由文本 | 无 | **新发现** |
| F7 | 无数据导出/留存管线 | `grep data_export\|retention_policy\|right_to_be_forgotten` 0 命中(仅菜单文案) | 00-INDEX PP-12 列了清单 | 已识别但未设计 |
| F8 | 脱敏硬编码 + 出口处调用 | `masking.rs` 硬编码 mask_id_number 前3后4, 仅 helper.rs 出口调用 | 无 | **新发现**(无策略表, 无 ABAC) |
## 3. 战略举措(归并 14 条提案为 4 项)
### I-1 PII 加密链路打通与 DEK 轮换闭环(合规地基)
**rationale**: F1+F4+F5 三处加密不对称, 让「PII 加密企业级」的宣称在测评员面前一戳即破——name 是唯一明文敏感 PII, DEK 版本恒为 1 让轮换按钮变成「点了就损坏历史数据」的潜伏地雷, erp-ai 全链路 dev KEK 更是「医疗化验单对任意代码读者明文」的硬伤。
**phases**:
1. **dev_default 编译期硬隔离** — 给 `erp-core/crypto/mod.rs:37` + `erp-health/crypto.rs:48` 的 dev_default 加 `#[cfg(any(debug_assertions, test))]`, release 构建编译失败; erp-ai 的 2 处调用改为从 AiState 注入 `Arc<PiiCrypto>`; 一次性脚本把 settings 表中 dev KEK 加密的 Provider key 用旧 KEK 解密 + 新 KEK 重加密。
2. **DEK 版本回溯解密** — 新增 `current_dek_for_tenant(tenant_id) -> (dek, key_version)`; 7 处 `Set(Some(1))` 改为真实 version; decrypt_field 按行 key_version 反查 DEK; LRU 缓存。
3. **name 加密 + name_hash 盲索引** — m000170 加 name_encrypted + name_hash; 复用 pii::encrypt; expand→migrate→contract 三步在线迁移; FHIR name 查询降级为 hash 精确 + 提示; 提供 feature flag 让国资体检中心可关掉加密满足特殊检索需求。
4. **回溯测试** — 轮换后 v2 写、v1 读、re-encrypt 迁移子命令全测。
**effortEstimate**: 5-7 人日(P1=1d / P2=2d / P3=2-3d / P4=1d)
**expectedImpact**: 消除唯一明文 PII 漏点 + 关闭「轮换即损坏」潜伏故障 + 关闭 AI 模块明文解密能力泄漏; 等保三级「数据机密性」从「宣称」升级为「可验证」。
**kpis**: [release 构建 0 处 dev_default 调用, 全仓 Sensitive-PII 列加密率 100%, DEK 轮换后历史数据可解密率 100%, PII 日志泄漏点 0]
**dependencies**: [P-3 数据分类分级引擎(确定哪些字段属 Sensitive), backfill 停机窗口或在线双写]
### I-2 数据分类分级引擎 + 字段级 ABAC 访问控制(可举证底座)
**rationale**: F8 显示脱敏是「硬编码 + 出口处一刀切」——医生看诊拿不到完整身份证号、运营拿到的密文要前端解。等保三级「分级保护 + 最小授权」要求按字段敏感度差异化控制, 且必须后端单点决策(前端硬编码脱敏在合规审计中判「未实施访问控制」)。
**phases**:
1. **分类分级元数据** — erp-core 新增 SensitivityLevel(Public/Internal/Confidential/Restricted/Sensitive-PII/Sensitive-Medical) + DataCategory 枚举; m000171 data_dictionary 表 或 Entity attribute 标注; 先覆盖 health+ai ~30 核心实体。
2. **CI 不变量测试**`#[test]` 断言所有标 Sensitive-* 的列必须满足三项之一(列名 _encrypted / 同名 _hash 盲索引 / RLS 强策略); 把「敏感字段未加密」变成编译/CI 失败。
3. **ABAC for PII** — m000172 pii_access_policy 表(role × field × reveal/mask/hash-only × purpose); handler 序列化前查策略(缓存 Redis, key=tenant+role, TTL 5min); 复用 mask_phone/mask_id_number + reveal_full; 每次reveal 写 audit_log 含 purpose。
4. **自动生成分类文档** — 输出 data_classification.md 作为测评呈堂材料; 前端从 `_pii_meta` 字段读呈现策略。
**effortEstimate**: 8-10 人日(分类标准需法务/医疗顾问参与)
**expectedImpact**: 把「分级保护」从口号变为机器可验证不变量; 脱敏从「上下文无关」升级为「医生 reveal / 运营 mask / 统计 hash-only」的合理工程; 产出测评呈堂材料。
**kpis**: [Sensitive-PII/Sensitive-Medical 字段 100% 标注, CI 分级不变量测试通过率 100%, 脱敏策略后端单点覆盖率 100%, 审计 purpose 字段填充率 100%]
**dependencies**: [I-1 P3 name 加密完成(确定最终敏感字段集), 法务/医疗顾问确认基因 vs 一般医疗诊断级别差异]
### I-3 数据主体权利履行闭环(个保法 §45/§47 + 病历留存)
**rationale**: F7 显示导出/删除/留存零实现。个保法 §45(可携带权)/§47(删除权)是硬性义务, 外资/跨境医疗租户和等保测评会专门查这两个端点。医疗行业特殊性: 删除权与病历法定留存(门诊 15 年 / 住院 30 年)冲突, 必须分级处理而非一刀切。
**phases**:
1. **导出 API**`GET /api/v1/health/patients/me/export` 聚合 patient+health_record+vital_signs+lab_report+consultation+device_readings+follow_up+appointment+consent+points; 输出 FHIR Bundle(JSON)+CSV 双格式; 异步任务复用 ai_analysis_queue; 产物 OSS 签名 URL 7 天 + 二次认证 + IP 绑定 + 下载次数限制 + 水印。
2. **分级删除/匿名化**`DELETE /api/v1/health/patients/me` 分级: 非法定数据(咨询历史/积分明细/AI 分析结果)立即匿名化或硬删除; 法定病历走 `retention_until` 字段 + 软删除; 引入 **anonymize 中间态**(去标识化保留统计价值)作为「删除权 vs 法定留存」的法律/技术平衡点。
3. **留存策略引擎** — m000173 retention_policy 表(entity/field/retain_days/action{delete|anonymize|archive}|scope); 接入 erp-server/tasks.rs 现有 spawn; 每日扫描 deleted_at+retain_days<now() 执行; 医疗 15 年 / 营销 5 年 / AI 日志 180 天 / 审计永久; 支持**租户级覆盖**(体检中心 vs 健康管理咨询差异化)。
4. **DPO 仪表盘** — 患者门户「我的数据权利」(小程序自助申请 + Web 医护代申请两通道); 操作写 audit_log(patient.data_exported / patient.erasure_requested); dry-run + 二级审批工作流(复用 erp-workflow); 宽限期 to_purge_at 7 天。
**effortEstimate**: 12-15 人日(P1=3d / P2=4d / P3=3d / P4=2-3d)
**expectedImpact**: 履行个保法 §45/§47 法定义务; 拿下外资/跨境医疗租户采购评估; anonymize 中间态兼顾合规与统计科研价值; 患者数据主权成为产品差异化。
**kpis**: [导出端点存在且通过 E2E, 删除权响应时效 ≤30 天(法定), 留存策略覆盖率 100% 核心实体, 误删事故 0(dry-run + 审批)]
**dependencies**: [I-2 分类分级(确定哪些字段属病历法定留存), 法务明确病历 vs 非病历字段清单, I-1 KEK 真实化(导出解密正确)]
### I-4 完整性举证与读操作审计(抗篡改 + 内部越权检测)
**rationale**: F3 显示 verify_hash_chain 零调用——审计哈希链「装了摄像头但不回放」, DBA 可整批重算 hash 篡改历史而不被发现, 等保测评员会质疑「自证清白」。F2 显示医护全跳过 consent——个保法对敏感信息处理无职业豁免, 只有「法定义务/紧急情况」豁免且必须显式记录。当前 AuditLog 只记写操作, 抓不到「内部人员越权查看」这一隐私事件主根因。
**phases**:
1. **哈希链实时验证** — 新增后台任务 `start_audit_chain_verifier(interval=3604s)` 调用 verify_hash_chain; 断裂即写 alerts + critical tracing::error + Prometheus `audit_chain_broken_count`; 关键操作(登录/权限变更/数据导出/批量删除)断裂 fail-closed 阻断同租户后续写; 首次运行做基线扫描避免上线即告警刷屏。
2. **外部锚点(WORM)** — 每日 ETL 取 audit_logs 当日最后 record_hash 写入 WORM 存储(腾讯云 COS 版本锁定桶 / 本地 append-only 文件 fallback); 提供 `verify_audit_chain(start,end)` 从外部锚点重放校验; 公开锚点桶地址。
3. **consent 法定豁免通道** — consent_check.rs 移除医护硬跳过, 改为「访问已撤回同意数据时强制填 access_purpose(急诊/法定义务/科研)」落审计; 提供急诊一键声明快速通道 + 事后审计(避免拖慢急诊流程)。
4. **读操作审计独立层** — 新增 `data_access_log` 表(独立于 audit_log, 按月分区同 device_readings 模式); 字段 viewer_user_id/role/patient_id/resource_table/access_purpose/consent_reference; trait DataAccessAuditor 注入所有读 PII handler; DPO 仪表盘异常访问检测(1h 查 200 患者 = 爬取告警); 异步批量写入控制性能。
**effortEstimate**: 10-12 人日(P1=2d / P2=2-3d / P3=2-3d / P4=4d)
**expectedImpact**: 审计从「自证清白」升级为「外部不可变锚点可举证」; 补齐等保三级明确要求而当前缺失的「读操作可审计」; 关闭医护 consent 特权黑洞。
**kpis**: [verify_hash_chain 接线后基线扫描 0 断裂, 读操作审计覆盖核心读 handler 100%, 医护访问已撤回 consent 数据记录率 100%, 异常访问告警 MTTD ≤1h]
**dependencies**: [PP-04 Alertmanager 通知出口(告警可达), WORM 存储基础设施(私有化客户需本地 fallback), I-2 ABAC purpose 字段复用]
## 4. 速赢(1-2 周可落地)
1. **dev_default 编译期硬隔离 + erp-ai KEK 注入**(I-1 P1, 1-2 人日) — 给 `erp-core/crypto/mod.rs:37` + `erp-health/crypto.rs:48``#[cfg(any(debug_assertions, test))]`; erp-ai 2 处调用改 AiState 注入。**收益**: 关闭「任意代码读者可解密医疗化验单」的硬伤, 推翻 wiki「已修复」误记, effort/impact 比最高。**风险**: 可能暴露其他漏网 dev_default 调用点, 需全仓扫描。
2. **审计哈希链接线 + 基线扫描**(I-4 P1, 1-2 人日) — `verify_hash_chain` 函数已实现, 只需 spawn 任务调用 + 首次基线扫描。**收益**: 补齐「完整性证明」能力, effort 极低(函数已存在), 推翻「装了摄像头但不回放」的纸老虎状态。
3. **PII 日志 PiiRedactLayer**(跨提案, 1 人日) — tracing-subscriber layer 统一拦截 name/phone/id_number/address/email 字段 + 写 test-log capturing 回归测试。**收益**: 根治 V2 审计修了仍残留的 PII 日志泄漏点, 从「逐文件删字段」升级为「框架级不变量」。
## 5. 主题级风险
1. **加密迁移不可逆** — name 加密 + DEK 轮换 + backfill 期间双列并存, 任一步骤失败可能导致历史数据永久不可解密。**缓解**: expand→migrate→contract 三步在线迁移 + dry-run + 低峰期 + 完整备份。
2. **删除权 vs 病历法定留存法律冲突** — 个保法 §47 与《医疗机构病历管理规定》直接对立, 误删病历触发反向违规。**缓解**: anonymize 中间态 + 法务明确分级清单 + 二级审批 + 宽限期。
3. **读操作审计性能与存储成本** — 每次读 PII 都写日志, 每月可能数 GB, 拖慢主路径。**缓解**: 异步批量写入 + Redis 缓冲 + 按月分区 + 按敏感度采样。
4. **WORM 存储私有化适配** — 单机部署的私有化客户无对象存储。**缓解**: 本地 append-only 文件 fallback + 每周导出。
5. **FHIR 互操作让步** — name 加密后 FHIR Patient.name 模糊查询语义弱化, 外部 FHIR 客户体验下降。**缓解**: 降级为 hash 精确 + 提示, feature flag 让国资体检中心可关闭加密。
6. **purpose-scoped consent 误伤 AI** — AI 模块复用 patient 数据, purpose 未覆盖会误伤现有功能。**缓解**: 默认 purpose=treatment 兼容 + ai_training/cross_border_share 作为可选授权。
## 6. 专家分歧调和(最终取舍)
| 分歧 | 一方立场 | 另一方立场 | 最终取舍 |
|------|---------|-----------|----------|
| **name 是否全量加密** | 后端/性能: FHIR 互操作 + decrypt-on-read 性能损耗 | 合规/DPO: 红线优先, 准标识符重识别风险 | **采纳加密 + feature flag**——默认加密满足合规, 国资体检中心可关闭; DTO 批量 decrypt + 缓存缓解性能; FHIR 降级 hash 精确。合规红线不让步, 但给特殊场景留逃生口。 |
| **数据导出是否 over-engineering** | 产品: 体检中心不会真用 | 合规: 法定义务 + 采购评估硬指标 | **采纳**——个保法 §45 是法定义务不是产品功能, 缺即判不合规; anonymize 中间态对统计科研有附加价值, 非纯成本。 |
| **审计外部锚点是否过度** | 安全: 内部哈希链已足够 | 合规: 内部哈希链自证清白无证明力 | **采纳外部锚点 + 本地 fallback**——等保三级「完整性」核心是「防内部篡改可举证」, 但私有化客户用本地 append-only 文件, 不强制对象存储。 |
| **医护跳过 consent 是否便利** | 业务: 急诊流程不能拖慢 | 合规/DPO: 无职业豁免, 须显式记录法定豁免理由 | **采纳法定豁免通道**——移除硬跳过, 改为急诊一键声明 + access_purpose 落审计; 平衡急诊时效与合规举证。 |
| **脱敏前端还是后端** | 前端: 按角色判断 | DPO/数据安全: 后端单点决策 + 审计 | **采纳后端 ABAC**——前端硬编码脱敏在合规审计中判「未实施访问控制」, 必须后端单点 + `_pii_meta` 元数据驱动前端呈现。 |
| **DEK 轮换上线前还是上线后** | DevOps: 先上线再轮换 | 数据安全: 轮换管线已损坏, 上线即定时炸弹 | **采纳上线前修复**——这是「生产即损坏」潜伏故障, 管理员误点轮换按钮即历史数据不可解密, 列为上线前 blocker。 |
| **数据销毁永久保留 vs 分级** | AI/产品: 永久保留支撑训练 | DPO/数据安全: 违反数据最小化 + 法定删除权 | **采纳分级留存**——医疗按法定期(15/30 年)、营销短期(5 年)、AI 日志(180 天), anonymize 兼顾科研与合规。 |
## 7. 路线(6-12 个月)
| 阶段 | 时间窗 | 举措 | 里程碑 |
|------|--------|------|--------|
| **T0 上线前 blocker** | 立即-2 周 | I-1 P1(dev_default 隔离)+ I-4 P1(哈希链接线)+ PiiRedactLayer | release 构建 0 dev_default / 哈希链基线扫描通过 / PII 日志 0 泄漏 |
| **T1 加密链路打通** | 1-2 月 | I-1 完整(name 加密 + DEK 轮换回溯) | Sensitive-PII 加密率 100% / 轮换可用 |
| **T2 分类分级 + ABAC** | 2-4 月 | I-2 完整(元数据 + CI 不变量 + ABAC 策略) | 测评呈堂材料输出 / 脱敏后端单点 |
| **T3 数据主体权利** | 3-6 月 | I-3 完整(导出 + 分级删除 + 留存引擎) | 个保法 §45/§47 端点可演示 / DPO 仪表盘 |
| **T4 完整性 + 读审计** | 4-8 月 | I-4 完整(外部锚点 + consent 法定豁免 + 读审计) | 等保三级测评可过 / 读操作异常检测上线 |
| **T5 跨境/DPIA 网关** | 6-12 月 | data_transfer_gateway + AI 调用数据最小化 | 外资/跨境租户合规通道预留 |
> 总投入估算: T0-T4 约 35-44 人日(渐进式, 不阻塞 V1 上线)。T5 为演进预留, 当前无跨境租户, 优先级低但架构位要留。