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 注入修复条目
126 lines
17 KiB
Markdown
126 lines
17 KiB
Markdown
# 医疗合规与数据治理 — 主题综合
|
||
|
||
> 主持主题: 满足等保 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 为演进预留, 当前无跨境租户, 优先级低但架构位要留。
|