OpenMed 脱敏引擎威胁模型:从资产分解到可执行滥用用例套件的纵深防御剖析
【免费下载链接】openmedLocal-first healthcare AI: clinical NER & HIPAA PII de-identification that runs 100% on-device. 2,200+ medical models, 21 languages, Apple MLX + Python, no cloud, no patient data leaving your network. Apache-2.0项目地址: https://gitcode.com/GitHub_Trending/ope/openmed
导读
本文是 OpenMed 脱敏(de-identification)路径——即 "redactor"——的结构化威胁分析。OpenMed 的安全目标是:在承认脱敏无法消除全部残余重识别风险的前提下,防止受支持的去标识化路径暴露受保护健康信息(PHI)或其他个人数据。任何导致标识符在脱敏后存活的缺陷都构成redaction bypass(脱敏绕过),按 SECURITY.md 的定义属于安全缺陷而非普通 bug。读完本文,你将掌握 OpenMed 的资产与信任边界划分、五类对手模型、七阶段去标识化攻击面、STRIDE 属性映射、AC-01 至 AC-11 的完整滥用用例目录及其缓解状态,以及如何通过可执行测试套件验证缓解措施。
1. 威胁模型在整个安全体系中的定位
该威胁模型是 OpenMed 安全工作的锚点,它枚举"去标识化承诺可能以何种方式失效",将每一个枚举出的失效模式映射到已有的或计划中的缓解措施,并标记残余缺口。它支撑着三类具体工作:
- 对抗性评测(adversarial-eval):通过攻击视角验证模型与流水线的真实泄漏行为;
- 模糊测试(fuzzing):追踪 DoS 类路径(如病态输入导致的资源膨胀);
- 协调披露(coordinated disclosure):新发现的绕过必须在修复后再公开。
同时,它服务于欧盟 AI 法案的鲁棒性/网络安全义务(路线图 S7.7)以及 S8.6 风险登记。
文档刻意与一套可执行的滥用用例套件配对——tests/unit/security/test_redactor_leakage_bypass.py。该套件只使用合成标识符,将已缓解的滥用类别驱动穿过真实的脱敏表面,并断言每个类别都被拦截。公开目录只在线索类别层面描述已知缺口,刻意省略未缓解绕过的可利用复现细节;后续报告遵循 SECURITY.md 中的漏洞报告流程。
2. 资产:攻击者想要什么,以及"损失"意味着什么
威胁模型首先定义五类受保护资产(对应 threat-model.md 第 1 节):
| 资产 | 保护对象 | 损失条件 |
|---|---|---|
| A1 — 直接标识符 | 姓名、MRN、SSN、电话/邮箱、地址、出生日期、账号/卡号/IBAN、设备 ID、生物识别 ID | 标识符在输出文本中存活(泄漏) |
| A2 — 准标识符 | 罕见日期、邮政编码、>89 岁年龄、可通过关联重识别的入院区间 | 即使没有单个直接标识符,输出仍可被组合或辅助关联重识别 |
| A3 — 代理/日期平移密钥 | patient_key日期平移的 HMAC 密钥材料、代理库映射、reversible_id密钥材料 | 攻击者越过文档化映射、代理库与密钥保管边界恢复或关联原始值 |
| A4 — 审计完整性 | 签名、可复现的审计报告(偏移、哈希、溯源、风险分数) | 审计报告被伪造或篡改,或其可复现性/HMAC 签名被攻破 |
| A5 — 运行产物 | 日志、缓存、临时文件、审计报告 | 原始 PHI、令牌、密钥或"已脱敏→原始"映射落入其中 |
注意 A2 的特殊性:即使输出中不存在任何单个直接标识符,通过准标识符的组合关联(linkage)仍可能重识别个体,因此脱敏并非"删除几个字段"那么简单。
3. 信任边界:本地优先的边界划分
OpenMed 是local-first的,这些边界与OPENMED_OFFLINE非目标(模型下载后无强制网络调用)以及AGENTS.md的本地优先规则保持一致:
- TB1 — 进程边界(设备端):所有脱敏都在操作者机器上的进程内运行。模型下载完成后,默认不要求也不发起网络调用;离线模式(openmed/core/offline.py)可以硬阻断出口流量。默认脱敏路径不会把 PHI 发送给网络对端;操作者自建集成与第三方模型服务是独立的信任决策。
- TB2 — 输入边界(不可信文本):输入文档是不可信的。它可能被攻击者控制,包含对抗性 Unicode、编码技巧、提示注入字符串或故意混淆的标识符。redactor 必须对敌意输入健壮,而不只是对噪声输入健壮。
- TB3 — 密钥保管边界:日期平移 HMAC 密钥、代理库映射、可逆 ID 密钥由操作者提供,默认不记录日志、不嵌入输出。带种子的
replace代理是数据最小化手段而非加密,不声称提供密码学意义上的不可逆性;带密钥的可逆性是授权密钥或映射持有者的属性。 - TB4 — 产物边界:日志、缓存、临时文件与审计报告是运行遥测。它们携带偏移、哈希、溯源与风险分数——绝不携带明文标识符或映射(见 docs/security/no-raw-phi-logging.md)。
- TB5 — 模型-代码边界:只有第一方 privacy-filter 组织才走
trust_remote_code自定义代码路径;不可信的模型标识符落入标准加载器,标准加载器绝不启用远程代码(_TRUSTED_PRIVACY_FILTER_PREFIXES,位于 openmed/core/pii.py)。这直接对应 STRIDE 中的权限提升威胁(见第 6 节)。
4. 对手模型:谁是主要威胁
| 对手 | 能力 | 是否在范围内 |
|---|---|---|
| ADV-1 — 输入作者 | 控制待脱敏文档的内容,可构造混淆标识符、对抗性 Unicode、注入分隔符、提示注入字符串;不能运行进程内代码 | 是——主要对手 |
| ADV-2 — 输出读者 | 只能看到脱敏输出(可能还有审计报告),尝试通过检查或辅助数据关联恢复标识符;不持有密钥 | 是 |
| ADV-3 — 产物拾荒者 | 读取留在磁盘上的日志、缓存、临时文件或审计产物 | 是 |
| ADV-4 — 密钥/映射窃贼 | 已窃取日期平移/可逆密钥材料或代理映射,想恢复受保护值 | 部分——保管是缓解手段;窃取后的恢复属于预期中的保管失败,而在无密钥/无映射情况下的非预期恢复仍在范围内 |
| 范围外 | 在 OpenMed 进程内执行代码、控制 ML 模型权重、物理主机被攻破、或访问托管端点 | 否(见 SECURITY.md 范围外条款) |
主要对手是 ADV-1:一个不可信的输入作者,试图通过让标识符看起来像检测器会忽略的东西(而人类或下游关联仍能读出)来把直接标识符偷运过 redactor。这解释了为何威胁模型的重心放在 Unicode 混淆、分隔符变异等输入侧攻击上。
5. 攻击面:七阶段去标识化流水线
公开入口点是openmed.deidentify(以及extract_pii),实现在 openmed/core/pii.py,由 openmed/core/pipeline.py 编排。相关阶段按顺序为:
① Normalize(纵深防御)。流水线先做 NFC 归一化并折叠空白(Pipeline.stage1_normalize),PII 路径在此基础上额外通过normalize_for_pii_detection(openmed/core/script_detect.py)折叠对抗性 Unicode:剥离零宽控制符与独立组合标记,把希腊/西里尔/全角易混淆字符(confusables)折叠为对应的拉丁形似字符。全部操作保持偏移不变——检测到的 span 通过DetectionNormalization.remap_span重映射回原始文本(script_detect.py),确保脱敏落在真实字符上。
② Detect(ML)。基于归一化文本,由 token-classification 模型检测标识符。
③ Smart-merge。正则语义单元合并把模型碎片(例如被切分到多个 token 的日期)重新合并为完整标识符(_apply_pii_smart_merging,位于 openmed/core/pii.py)。
④ Safety sweep(确定性)。safety_sweep(openmed/core/safety_sweep.py,任务OM-008)在归一化文本上运行带验证器门控的正则(Luhn、IBAN、SSN、NPI、电话、邮箱……),把模型漏掉的结构化标识符追加进来。关键规则:已有模型 span 永远优先,与既有 span 重叠的扫描候选被丢弃。从源码看,候选按confidence → priority → start → 长度排序后逐条与活动 span 做重叠检查(safety_sweep.py),最终交给质量门做重叠解析。
⑤ Resolve overlaps / quality gate。resolve_overlapping_entities与validate_entity_spans(openmed/core/quality_gates.py,任务OM-012)产生确定性、无重叠、边界合法的 span 集合;冲突时关键/高风险标签胜出(源码中_CRITICAL_LABELS覆盖 SSN、PASSWORD、PIN、API_KEY、CREDIT_CARD、CVV,quality_gates.py)。值得注意的是 span 校验是warn-only:只记录诊断与元数据标志,绝不静默丢弃实体(quality_gates.py),避免"校验器反而造成泄漏"。
⑥ Redact。每个 span 按_redact_entity(openmed/core/pii.py)执行 mask / remove / replace / hash / date-shift / format-preserve,用替换内容覆盖输出中的原始字符。deidentify的完整签名(pii.py)暴露了安全相关的关键参数:confidence_threshold默认 0.7(注释明确"Higher threshold for safety")、patient_key/date_shift_secret(HMAC 密钥材料,原始密钥不落日志)、keep_mapping、use_safety_sweep(默认 True)、consistent/seed(可预测代理的控制开关)。
⑦ Audit(可选)。可复现的AuditReport记录偏移、哈希、溯源、阈值与来自risk_report的残余风险摘要(openmed/risk/init.py),且不存储源 span 或周围上下文的明文。
安全扫描与归一化层是关键的纵深防御:对显式支持的转换与结构化格式,归一化先规范化输入,确定性扫描再回收模型漏掉的标识符。但它们并不覆盖所有可能的混淆——这正是滥用用例目录存在的意义。
6. STRIDE 风格映射
| 威胁属性 | 作用于 redactor | 主要控制 |
|---|---|---|
| Spoofing(仿冒) | 易混淆/混合脚本字符伪装成良性 token,让检测器跳过 | 易混淆折叠 + 脚本一致性摘要(normalize_for_pii_detection) |
| Tampering(篡改) | 注入零宽/空白/标点篡改标识符表面形态 | 零宽/组合标记剥离;空白折叠;smart-merge;安全扫描 |
| Repudiation(抵赖) | 操作者无法证明"脱敏了什么、为什么" | 带溯源、阈值、manifest 哈希的可复现AuditReport |
| Information disclosure(信息泄露) | 标识符存活到输出;或 PHI 泄漏进日志/缓存/审计 | 整个 detect→sweep→redact 链;no-raw-PHI 日志策略;哈希化审计 span |
| Denial of service(拒绝服务) | 病态输入膨胀工作量或使脱敏崩溃,导致操作者关闭它 | 保持偏移、有界的归一化;warn-only span 校验(绝不丢弃)。超出主范围,为模糊测试任务跟踪 |
| Elevation of privilege(权限提升) | 不可信模型标识符触发trust_remote_code | 仅第一方前缀白名单(_TRUSTED_PRIVACY_FILTER_PREFIXES) |
7. 滥用用例目录:AC-01 至 AC-11
每个滥用用例记录向量类别、攻击者目标、缓解(或缺口)与状态。AC 编号是稳定的,所有已发布示例均为合成数据。
| ID | 滥用用例 | 向量 | 缓解 | 状态 |
|---|---|---|---|---|
| AC-01 | 零宽/空白拆分标识符 | 在 SSN/卡号/邮箱内插入零宽连接符或散落空格,使 ML token 与正则同时断裂 | normalize_for_pii_detection剥离零宽控制符;空白变体由扫描正则匹配;smart-merge 重并 ML 碎片;safety_sweep最终回收 | 已缓解 |
| AC-02 | 未规范化的分隔符变异 | 某些可见分隔符变异可能破坏结构化标识符匹配;文档刻意省略可利用形态与复现细节 | 不声称有完整确定性缓解;ML 检测器可提供纵深防御但不视为保证控制 | 已知缺口 |
| AC-03 | Unicode 易混淆/混合脚本混淆 | 用希腊/西里尔/全角形似字符替换标识符字符(如janе.doe@…中的西里尔е) | 检测前易混淆折叠为拉丁;混合脚本在元数据中标记;span 重映射回原始文本 | 已缓解 |
| AC-04 | 全角数字编码 | 用全角数字(4111 …)书写标识符,躲避 ASCII 数字正则 | 全角形态(U+FF01–FF5E)在扫描前被折叠为 ASCII | 已缓解 |
| AC-05 | 组合标记混淆 | 在标识符字符上叠加独立组合变音符号 | 类别Mn组合标记在检测前被保持偏移地剥离 | 已缓解 |
| AC-06 | 分块边界/偏移拆分 | 模型在分块边界把一个标识符拆成两个相邻 span,每个都低于脱敏阈值 | _apply_pii_smart_merging在脱敏前把相邻碎片合并为一个语义单元 | 已缓解 |
| AC-07 | 校验和无效诱饵 vs 有效标识符 | 攻击者混合校验和无效诱饵与有效标识符,希望验证器噪声掩盖真标识符 | 扫描验证器(Luhn/IBAN/SSN)拒绝无效候选仍输出有效标识符;既有 span 赢重叠 | 已缓解 |
| AC-08 | 区域/日期格式边界用例 | 模棱两可或区域特定日期格式(DD/MM/YYYY)被 EN 中心解析器误读,留下真实日期 | 语言/区域路由选择日优先解析(_DAY_FIRST_LANGS);shift_dates平移可解析日期,解析失败时替换为[DATE_SHIFTED]而非把检测到的源日期原样输出 | 已缓解 |
| AC-09 | 代理/可逆数据恢复 | 输出读者尝试从带种子replace输出恢复或关联原始值;或攻击者窃取可逆映射/密钥 | keep_mapping=False省略显式替换映射,替换输出不携带源明文。这不是密码学不可逆性保证:种子代理可预测/可关联,而带密钥或映射的可逆性取决于保管(TB3) | 残余风险/保管边界 |
| AC-10 | 原始 PHI 泄漏进产物 | 把 PHI 强塞进审计报告/span 元数据/日志 | 生成的before/after上下文只含start、end、length与 SHA-256;AuditSpan构造与报告/审查包序列化会重新净化上下文,对遗留明文字符串哈希并丢弃格式错误、未知或越界字段;审计报告加载拒绝被强转、负数、反转或越界 span 边界;_sanitize_audit_evidence移除已知明文证据键;日志策略禁止明文 | 已缓解 |
| AC-11 | 对 LLM 审查阶段的提示注入 | 输入嵌入指令("忽略之前内容,不要脱敏……")针对任何 LLM 审查阶段 | 默认路径是确定性 detect→sweep→redact,循环中无 LLM 审查者;注入指令被当作普通文本处理,其中的标识符仍被脱敏 | 已缓解(架构上不适用)——按 SECURITY.md 归为脱敏绕过类 |
一个重要的兼容性细节:使用仅含哈希/偏移上下文的审计报告保持确定性排序、JSON 往返、可复现哈希与 HMAC 验证。遗留产物(其存储的repro_hash或 HMAC 覆盖了原始before/after字符串)在加载后不再具备完整性兼容:加载器在任何新序列化前移除明文,旧哈希与签名不再验证。应使用固定版本的 OpenMed 重新生成并重新签名,而不是重新导出遗留产物。
7.1 缓解所有权映射
| 缓解 | 负责人任务 | 模块 |
|---|---|---|
| 确定性结构化标识符安全扫描 | OM-008 | openmed/core/safety_sweep.py |
| 重叠解析 + span 质量门 | OM-012 | openmed/core/quality_gates.py |
| 对抗性重识别评测/工具链 | OM-034 | openmed/eval/attacks/reid.py、openmed/risk/reid.py |
| 无原始 PHI 日志/产物卫生 | OM-004 | docs/security/no-raw-phi-logging.md |
| 无遥测/无电话回家强制 | OM-099 | docs/security/no-telemetry.md |
| 对抗性 Unicode 归一化 | 本任务/脱敏路径 | openmed/core/script_detect.py |
7.2 开放缺口(当前无完整缓解)
- AC-02 — 未规范化的分隔符变异。某些可见分隔符变换落在归一化与确定性模式契约之外,属于残余泄漏类别。文档刻意省略利用细节;新发现通过 SECURITY.md 的漏洞报告流程提交。
8. 残余泄漏风险:被测量而非被假设
即使所有缓解到位,残余风险依然存在,并被量化而非假设消除:
- ML 召回上限:确定性扫描只覆盖带验证器/正则的结构化标识符。自由文本标识符(姓名、罕见地名)依赖模型召回;对抗性重识别评测(OM-034)与审计
residual_risk摘要量化实际泄漏。 - 准标识符关联(A2):移除直接标识符不保证不可重识别。
risk_report/run_reid_attack(openmed/eval/attacks/reid.py)对关联与 k-匿名评分;k_min == 1的记录被标记为完全可重识别。 - 新型混淆向量:尚未折叠的新易混淆区间或拆分技巧可能绕过检测。滥用用例套件是已缓解类别的回归网;新类别获得一个AC编号,但不会发布未修补的漏洞利用配方。
- 密钥保管(A3):可逆性强度取决于操作者密钥保管;OpenMed 无法防御被窃取的密钥。
- 种子替换的可预测性:
replace移除源明文并可省略显式映射,但确定性 Faker 代理不是加密。跨文档可关联性或通过可预测输入恢复,应视为残余风险而非密码学不可逆性。
9. 如何执行该模型:可运行滥用用例套件
滥用用例目录是可执行的。运行:
.venv/bin/python -m pytest tests/unit/security/test_redactor_leakage_bypass.py -q已缓解的AC-*类别驱动合成尝试穿过真实脱敏表面(safety_sweep、normalize_for_pii_detection、以及使用 mock 检测器的deidentify),断言标识符被拦截。该套件有三条硬性规则(见 test_redactor_leakage_bypass.py 文档字符串):
- 仅合成标识符——数字被构造成结构上有效(校验和正确)但不属于任何真实个人(如 Luhn 有效的 Visa 测试卡号
4111 1111 1111 1111、文档用 SSN 形态123-45-6789、IBAN 注册表示例GB82 WEST 1234 5698 7654 32)。 - 离线——确定性层(
normalize_for_pii_detection→safety_sweep)不需要模型;端到端deidentify路径把 ML 检测器 mock 成盲检测器(什么也找不到),这是最强的测试:它证明即使模型完全失效,redactor 的承诺在确定性结构化标识符上依然成立。 - 泄漏优先——断言检查的是原始标识符不存活于输出,而不只是"找到了某个实体"。
值得注意的实现细节:AC-01 测试先证明"裸扫描会被绕过"(credit_debit_card不在直接扫描结果中),再证明"归一化链能回收"——这正是归一化层存在的理由。AC-10 测试则遍历审计产物的三种序列化形态(repr(to_dict())、to_json()、export_review_bundle()),逐一断言原始标识符与明文上下文均不出现,同时确认审计仍以text_hash形式记录了相邻标识符被找到的事实——"记录证据但不记录明文"的平衡点。
当前公开回归套件刻意省略已知未缓解类别的复现;公开回归应伴随协调修复与披露一起落地,而非提前。已知缺口(AC-02)或新怀疑的绕过,请通过 SECURITY.md 私有渠道报告,附合成复现——绝不要公开发 issue,绝不要使用真实数据(SECURITY.md 明确要求:报告泄漏真实数据本身即事件,须按事件处理)。
10. 配套文档与延伸阅读
- 无原始 PHI 日志策略的完整允许/禁止清单与工程要求见 docs/security/no-raw-phi-logging.md(其中规定了在修改 PII/脱敏路径后运行
pytest tests/unit/test_no_raw_text_logging.py -q的守卫测试)。 - 漏洞分级(CVSS v4.0 + 隐私影响覆盖,隐私缺陷从不低于 High)、响应时间目标(3 个工作日确认、90 天协调披露)与安全港条款见 SECURITY.md。
- 对抗性重识别评测的量化实现见 openmed/eval/attacks/reid.py 与 openmed/risk/reid.py。
总而言之,OpenMed 的 redactor 威胁模型把"脱敏不可能绝对安全"这一现实转化为一套可审计、可执行、可演进的工程体系:资产与信任边界定义保护范围,STRIDE 映射确定控制归属,AC 目录把攻击向量固化为回归测试,而残余风险章节则诚实地标注出测量得到而非假设掉的安全边界。对任何将 OpenMed 集成进临床或数据处理管线的工程师而言,这篇模型既是威胁认知基线,也是漏洞报告与验收测试的操作手册。
【免费下载链接】openmedLocal-first healthcare AI: clinical NER & HIPAA PII de-identification that runs 100% on-device. 2,200+ medical models, 21 languages, Apple MLX + Python, no cloud, no patient data leaving your network. Apache-2.0项目地址: https://gitcode.com/GitHub_Trending/ope/openmed
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考