Ch 18 数据脱敏与隐私治理¶
面包屑
本书主页 › Part III 数据工程实践 › Ch 18
项目第 1 年 · 核心建设期——合规治理嵌入
本章你将学到¶
- 匿名化策略矩阵:脱敏、部分脱敏、加密、哈希的适用场景
- 配置驱动的字段级脱敏设计,以及医药行业字段脱敏决策表与脱敏 UDF 实现
- KMS 信封加密与密钥分层、DSR 数据主体权利响应、脱敏验证
- Redshift RLS/CLS 与脱敏 UDF 的三层协同防护体系
- 医药 GxP 数据完整性与中国数据合规(PIPL)的引申
医药行业的数据治理,比我在专利数据和企业征信两段经历里遇到的都要严格。
专利数据的敏感度相对低——专利本身就是公开文件,只有少量商业秘密(如未公开的专利申请)需要保护。企业征信涉及企业敏感信息,但主要是"企业级"的,个人隐私数据较少。但医药行业不一样——它同时涉及企业敏感数据(处方量、市场份额、定价策略)和个人隐私数据(患者姓名、身份证号、病历、处方记录),而且受 GxP 法规和中国 PIPL 双重约束。数据泄露不仅是技术事故,更是合规事故,可能导致监管处罚和声誉损失。
所以在 Aurora 平台设计之初,我就在 Ch 2 的范围边界里把"治理合规"列为平台五点核心诉求之一。这一章就是把"治理合规"从口号变成工程实现——匿名化策略矩阵、配置驱动脱敏、RLS/CLS/脱敏三层协同防护。
18.1 匿名化策略矩阵:脱敏、部分脱敏、加密、哈希¶
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#edf5ff','primaryTextColor':'#161616','primaryBorderColor':'#0f62fe','lineColor':'#697077','secondaryColor':'#d9fbfb','tertiaryColor':'#f2f4f8'}}}%%
flowchart TB
classDef bpProcess fill:#edf5ff,stroke:#0f62fe,stroke-width:2px,color:#161616
classDef bpDecision fill:#fcf4d6,stroke:#f1c21b,stroke-width:2px,color:#161616
subgraph 策略层["匿名化策略"]
MASK[完全脱敏<br/>mask → ******]
PARTIAL[部分脱敏<br/>partial_mask → 138****5678]
ENCRYPT[可逆加密<br/>aes_encrypt → 密文]
HASH[不可逆哈希<br/>md5 → 固定长度摘要]
CLEAR[不处理<br/>clear → 原值]
end
subgraph 场景层["适用场景"]
MASK_USE[极敏感且无需分析<br/>患者姓名]
PARTIAL_USE[需保留格式用于分析<br/>手机号]
ENCRYPT_USE[需要还原的场景<br/>身份证号]
HASH_USE[需跨源关联但不需原值<br/>患者 ID]
CLEAR_USE[非敏感字段<br/>医院名称]
end
MASK -->|适合| MASK_USE
PARTIAL -->|适合| PARTIAL_USE
ENCRYPT -->|适合| ENCRYPT_USE
HASH -->|适合| HASH_USE
CLEAR -->|适合| CLEAR_USE
class MASK,PARTIAL,ENCRYPT,HASH,CLEAR bpProcess
class MASK_USE,PARTIAL_USE,ENCRYPT_USE,HASH_USE,CLEAR_USE bpDecision
图 18-1 匿名化策略矩阵:脱敏、部分脱敏、加密、哈希
| 策略 | 机制 | 可逆性 | 适合字段 | 举例 |
|---|---|---|---|---|
| 完全脱敏 | 替换为固定值 | 不可逆 | 极敏感且无需分析 | 患者姓名 → ****** |
| 部分脱敏 | 保留部分、遮蔽部分 | 不可逆 | 需保留格式用于分析 | 手机号 → 138****5678 |
| 可逆加密 | AES 加密 | 可逆(需密钥) | 需要还原的场景 | 身份证号 → 密文 |
| 不可逆哈希 | MD5/SHA 哈希 | 不可逆 | 需跨源关联但不需原值 | 患者 ID → 哈希值 |
| 不处理 | 原值保留 | — | 非敏感字段 | 医院名称 |
表 18-1 匿名化策略矩阵:脱敏、部分脱敏、加密、哈希
引申
选择脱敏策略的核心问题是"这个字段需要用于分析吗?需要还原吗?需要跨源关联吗?"——这三个问题决定了策略选择。比如患者身份证号:如果要跨源关联但不需原值 → 哈希;如果合规要求可审计还原 → 加密;如果完全不需要 → 完全脱敏。
18.2 配置驱动的字段级脱敏设计¶
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#edf5ff','primaryTextColor':'#161616','primaryBorderColor':'#0f62fe','lineColor':'#697077','secondaryColor':'#d9fbfb','tertiaryColor':'#f2f4f8','fontSize':'14px'}}}%%
flowchart LR
classDef bpProcess fill:#edf5ff,stroke:#0f62fe,stroke-width:2px,color:#161616
classDef bpData fill:#d9fbfb,stroke:#007d79,stroke-width:2px,color:#161616
classDef bpDecision fill:#fcf4d6,stroke:#f1c21b,stroke-width:2px,color:#161616
classDef bpSuccess fill:#defbe6,stroke:#198038,stroke-width:2px,color:#161616
classDef bpError fill:#fff1f1,stroke:#da1e28,stroke-width:2px,color:#161616
classDef bpExternal fill:#f2f4f8,stroke:#697077,stroke-width:2px,color:#161616
classDef bpInfo fill:#f6f2ff,stroke:#8a3ffc,stroke-width:2px,color:#161616
linkStyle default stroke:#697077,stroke-width:2px,fill:none
subgraph 配置驱动脱敏["配置驱动的字段级脱敏"]
CONFIG[任务配置中的脱敏规则<br/>字段 → 策略映射]
ENGINE[ETL 引擎<br/>Enriched 加工时执行]
UDF[脱敏 UDF<br/>mask/partial/encrypt/hash]
end
CONFIG --> ENGINE
ENGINE -->|按规则调用| UDF
UDF -->|输出脱敏后的值| ENRICHED[Enriched 层<br/>已脱敏数据]
class CONFIG,ENGINE,UDF bpProcess
class ENRICHED bpData
图 18-2 配置驱动的字段级脱敏设计
脱敏规则放在任务配置里,声明"哪个字段用什么策略":
// 脱敏规则配置示意
"masking_rules": {
"patient_name": "mask",
"phone": "partial_mask",
"id_card": "aes_encrypt",
"patient_id": "md5"
}
但"哪个字段用哪种策略"不能拍脑袋——得看字段的敏感级别、分析用途、要不要还原、要不要跨源关联。下面这张医药行业字段脱敏决策表把 18.1 引申框里的三个问题(要分析吗?要还原吗?要跨源关联吗?)落到具体字段,作为脱敏规则配置的依据:
| 数据类别 | 典型字段 | 敏感级别 | 分析用途 | 可逆需求 | 跨源关联 | 选用策略 | 理由 |
|---|---|---|---|---|---|---|---|
| 患者标识 | 受试者编号、患者姓名、身份证号 | P0 极敏感 | 需聚合统计 | 否 | 是(跨系统关联同一患者) | md5 | 需跨源关联但不可还原身份 |
| 患者敏感 | 诊断详情、不良反应报告 | P0 极敏感 | 临床分析 | 是(授权解密) | 否 | aes_encrypt + RLS | 合规要求可还原但严格控权 |
| 医生标识 | 处方医生姓名、医师执业证号 | P1 敏感 | 医生维度分析 | 否 | 是 | partial_mask(保留科室/医院) | 需分析但限制个人识别 |
| 药品定价 | 净价、折扣率、返利比例 | P1 敏感 | 商务分析 | 是(财务角色解密) | 否 | aes_encrypt | 商业敏感,严格列级权限 |
| 处方量排名 | 医生-产品-月度处方数 | P1 敏感 | 绩效分析 | 否 | 否 | md5(医生ID) + 聚合至医院级 | 防止反推识别个人 |
| HCP 主数据 | 医生姓名、联系方式、科室 | P1 敏感 | 主数据治理 | 否 | 是 | clear(内部员工)/ partial_mask(外部) | 分级可见 |
| 渠道库存 | 经销商编码、进货/库存数量 | P2 内部 | 商务分析 | 否 | 否 | clear(内部商务)/ mask(跨部门) | 竞争敏感 |
| 临床试验 | 随机分组、盲法编码 | P0 极敏感 | 揭盲分析 | 揭盲时是 | 否 | md5(分析)/ aes_encrypt(揭盲流程) | 盲法完整性 |
| 患者反馈 | 自由文本投诉/评价 | P1 敏感 | 文本分析 | 否 | 否 | 脱敏 NER(识别姓名/电话/地址后 mask) | 非结构化脱敏 |
表 18-2 配置驱动的字段级脱敏设计
脱敏 UDF 实现与密钥供给¶
决策表定好策略后,每种策略对应一个 UDF。mask/partial_mask/md5 纯计算不用密钥,但 aes_encrypt 需要数据密钥——密钥从哪来、怎么不随代码泄露,这是脱敏工程的关键:
# 示意:四种脱敏 UDF(aes_encrypt 的密钥来自 KMS 信封加密)
import hashlib, boto3
from cryptography.fernet import Fernet
kms = boto3.client("kms")
def mask(value): return "******"
def partial_mask(phone): return phone[:3] + "****" + phone[-4:] # 138****5678
def md5_hash(value): return hashlib.md5(str(value).encode()).hexdigest()
def aes_encrypt(value, cmk_id="ap-aurora-cdp-enriched-cmk"):
# 核心意图:信封加密——KMS 解密 data key,本地加密数据,data key 不落盘不进代码
data_key = kms.generate_data_key(KeyId=cmk_id, KeySpec="AES_256")["Plaintext"] # 仅内存
cipher = Fernet(Fernet.generate_key_from_bytes(data_key)).encrypt(value.encode())
del data_key # 用完即焚
return cipher.decode()
KMS 信封加密与密钥分层¶
上面的 aes_encrypt UDF 用到了信封加密(Envelope Encryption):一个客户主密钥(CMK,永不出 KMS)加密数据密钥(Data Key),数据密钥在内存里加密数据,用完即焚。密钥从不随数据落盘,也从不硬编码在 ETL 代码里:
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#edf5ff','primaryTextColor':'#161616','primaryBorderColor':'#0f62fe','lineColor':'#697077','secondaryColor':'#d9fbfb','tertiaryColor':'#f2f4f8','fontSize':'14px'}}}%%
flowchart LR
classDef bpProcess fill:#edf5ff,stroke:#0f62fe,stroke-width:2px,color:#161616
classDef bpData fill:#d9fbfb,stroke:#007d79,stroke-width:2px,color:#161616
classDef bpDecision fill:#fcf4d6,stroke:#f1c21b,stroke-width:2px,color:#161616
classDef bpSuccess fill:#defbe6,stroke:#198038,stroke-width:2px,color:#161616
classDef bpError fill:#fff1f1,stroke:#da1e28,stroke-width:2px,color:#161616
classDef bpExternal fill:#f2f4f8,stroke:#697077,stroke-width:2px,color:#161616
classDef bpInfo fill:#f6f2ff,stroke:#8a3ffc,stroke-width:2px,color:#161616
linkStyle default stroke:#697077,stroke-width:2px,fill:none
CMK[KMS CMK<br/>ap-aurora-cdp-enriched-cmk<br/>永不出 KMS] -->|加密/解密| DK[Data Key<br/>仅内存]
DK -->|加密| DATA[敏感字段密文<br/>落 Enriched/S3]
DK -.->|用完即焚| X[不落盘]
class CMK bpData
class DK bpProcess
class DATA bpData
class X bpError
图 18-3 KMS 信封加密与密钥分层
| 密钥层 | 用途 | 存放 | 谁能调用 |
|---|---|---|---|
| CMK(主密钥) | 加密/解密 Data Key | KMS(永不导出) | ETL 角色(限 IAM) |
| Data Key(数据密钥) | 加密字段值 | 仅内存,用完即焚 | UDF 进程内 |
| 密文字段 | 落 Enriched/S3 | S3(KMS 加密) | 分析师(密文不可读) |
表 18-3 KMS 信封加密与密钥分层
CMK 的创建、轮换、策略由 IaC 管理,定义在 Ch 22 的 core-infra 仓库——脱敏 UDF 只负责"用",不负责"管",职责分离。
DSR 数据主体权利响应¶
PIPL 赋予患者"删除权"等数据主体权利(DSR, Data Subject Request)。当患者行使删除权时,平台不能只删一张表——患者 ID 散落在 Landing/Raw/Enriched/Redshift 多层多表。DSR 响应需要一套基于血缘的分层删除流程:
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#edf5ff','primaryTextColor':'#161616','primaryBorderColor':'#0f62fe','lineColor':'#697077','secondaryColor':'#d9fbfb','tertiaryColor':'#f2f4f8','fontSize':'14px'}}}%%
flowchart TD
classDef bpProcess fill:#edf5ff,stroke:#0f62fe,stroke-width:2px,color:#161616
classDef bpData fill:#d9fbfb,stroke:#007d79,stroke-width:2px,color:#161616
classDef bpDecision fill:#fcf4d6,stroke:#f1c21b,stroke-width:2px,color:#161616
classDef bpSuccess fill:#defbe6,stroke:#198038,stroke-width:2px,color:#161616
classDef bpError fill:#fff1f1,stroke:#da1e28,stroke-width:2px,color:#161616
classDef bpExternal fill:#f2f4f8,stroke:#697077,stroke-width:2px,color:#161616
classDef bpInfo fill:#f6f2ff,stroke:#8a3ffc,stroke-width:2px,color:#161616
linkStyle default stroke:#697077,stroke-width:2px,fill:none
REQ[患者行使删除权<br/>提供患者 ID] --> LINEAGE[① 元数据血缘反追<br/>患者 ID → 所有关联表]
LINEAGE --> SEQ[② 分层删除序列]
SEQ --> S1[Redshift 查询层<br/>软删除标记]
SEQ --> S2[S3 Enriched<br/>分区覆盖重写]
SEQ --> S3[S3 Raw<br/>分区级删除]
SEQ --> S4[Landing 层<br/>保留审计但标记已删除]
SEQ --> REPORT[③ 生成 DSR 执行报告<br/>时间戳/操作者/影响行数]
class REQ bpInfo
class LINEAGE,SEQ bpProcess
class S1,S2,S3 bpData
class S4 bpData
class REPORT bpSuccess
图 18-4 DSR 数据主体权利响应
| 步骤 | 做法 | 理由 |
|---|---|---|
| ① 血缘反追 | 通过元数据血缘(Ch 20)找到患者 ID 出现在哪些表 | 避免遗漏 |
| ② 分层删除 | Redshift 软删除 → Enriched 分区覆盖 → Raw 分区删除 → Landing 保留但标记 | 从消费端到源头逐层清理;Landing 保留以满足 GxP 审计要求 |
| ③ 审计报告 | 生成含时间戳、操作者、影响行数的 DSR 报告 | 供合规审查,证明删除已执行 |
表 18-4 DSR 数据主体权利响应
引申
DSR 响应是"被动血缘"价值最直接的体现——如果没有血缘,DSR 只能靠人工猜"患者数据可能在哪几张表",漏删风险极高。这也是为什么 Ch 20 和 Ch 52 都把"主动血缘"列为遗憾——血缘不只是排障工具,更是合规基础设施。
脱敏验证¶
脱敏策略配好后,必须验证它"真的生效了"——医药合规审计会检查脱敏能不能绕过。验证包括三类测试:
| 验证类型 | 做法 | 目的 |
|---|---|---|
| 不可逆性检查 | 对 md5/mask 后的值尝试反推原值 | 确认哈希/脱敏不可还原 |
| 格式保留检查 | 检查 partial_mask 后格式是否一致(如手机号仍 11 位) | 确认脱敏不破坏下游分析依赖的格式 |
| 重识别攻击测试 | 用准标识符(邮编+生日+性别)组合尝试重新识别个体 | 确认脱敏后数据不可被"拼图式"去匿名化 |
表 18-5 脱敏验证
# 示意:脱敏验证——重识别攻击测试
def reidentification_risk(df, quasi_identifiers=["zip", "birth_date", "gender"]):
# 核心意图:准标识符组合越唯一,重识别风险越高
groups = df.groupBy(*quasi_identifiers).count()
risky = groups.filter("count = 1").count() / df.count() # 唯一组合占比
return risky # 行业基准:<0.2 为低风险,>0.5 需加强泛化
| 设计要点 | 说明 |
|---|---|
| 声明式 | 脱敏规则在配置中声明,不在代码中硬编码 |
| 字段级 | 精确到每个字段的脱敏策略 |
| UD 化 | 每种策略实现为可复用的 UDF(用户定义函数) |
| 不可绕过 | Enriched 加工时强制执行,确保下游拿到的是脱敏后数据 |
表 18-6 示意:脱敏验证——重识别攻击测试
Trade-off
脱敏在 Enriched 层执行意味着 Raw 层仍保留原始敏感数据。这是必要的——如果脱敏后发现策略错了(比如该哈希的用了 mask),需要从 Raw 层重跑。Raw 层的敏感数据通过 IAM 权限严格控制(只有 ETL 引擎能访问),不暴露给分析师。
18.3 Redshift RLS/CLS 与脱敏 UDF 的协同防护体系¶
结合 Ch 8 介绍的 RLS/CLS,平台构建了三层纵深防御:
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#edf5ff','primaryTextColor':'#161616','primaryBorderColor':'#0f62fe','lineColor':'#697077','secondaryColor':'#d9fbfb','tertiaryColor':'#f2f4f8','fontSize':'14px'}}}%%
flowchart TB
classDef bpProcess fill:#edf5ff,stroke:#0f62fe,stroke-width:2px,color:#161616
classDef bpData fill:#d9fbfb,stroke:#007d79,stroke-width:2px,color:#161616
classDef bpDecision fill:#fcf4d6,stroke:#f1c21b,stroke-width:2px,color:#161616
classDef bpSuccess fill:#defbe6,stroke:#198038,stroke-width:2px,color:#161616
classDef bpError fill:#fff1f1,stroke:#da1e28,stroke-width:2px,color:#161616
classDef bpExternal fill:#f2f4f8,stroke:#697077,stroke-width:2px,color:#161616
classDef bpInfo fill:#f6f2ff,stroke:#8a3ffc,stroke-width:2px,color:#161616
linkStyle default stroke:#697077,stroke-width:2px,fill:none
QUERY[SQL 查询请求] --> L1
subgraph 三层纵深防御["三层纵深防御"]
L1[第一层:RLS 行级安全<br/>按角色/区域/租户控制行可见性<br/>WHERE 条件自动注入]
L2[第二层:CLS 列级安全<br/>按角色控制列可见性<br/>GRANT 列级权限]
L3[第三层:脱敏 UDF 值级<br/>即使看到字段,值已脱敏<br/>mask/hash/encrypt]
end
L1 --> L2 --> L3 --> RESULT[最终返回的数据]
class QUERY bpExternal
class L1,L2,L3 bpProcess
class RESULT bpSuccess
图 18-5 Redshift RLS/CLS 与脱敏 UDF 的协同防护体系
三层协同的实战场景¶
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#edf5ff','primaryTextColor':'#161616','primaryBorderColor':'#0f62fe','lineColor':'#697077','secondaryColor':'#d9fbfb','tertiaryColor':'#f2f4f8','fontSize':'14px'}}}%%
flowchart TD
classDef bpProcess fill:#edf5ff,stroke:#0f62fe,stroke-width:2px,color:#161616
classDef bpData fill:#d9fbfb,stroke:#007d79,stroke-width:2px,color:#161616
classDef bpDecision fill:#fcf4d6,stroke:#f1c21b,stroke-width:2px,color:#161616
classDef bpSuccess fill:#defbe6,stroke:#198038,stroke-width:2px,color:#161616
classDef bpError fill:#fff1f1,stroke:#da1e28,stroke-width:2px,color:#161616
classDef bpExternal fill:#f2f4f8,stroke:#697077,stroke-width:2px,color:#161616
classDef bpInfo fill:#f6f2ff,stroke:#8a3ffc,stroke-width:2px,color:#161616
linkStyle default stroke:#697077,stroke-width:2px,fill:none
subgraph SG1["场景:华东分析师查询处方数据"]
S1[① RLS 过滤<br/>自动加 WHERE region='华东'<br/>→ 只看到华东区行]
S2[② CLS 过滤<br/>患者身份证列无权限<br/>→ 看不到该列]
S3[③ 脱敏过滤<br/>患者姓名已 mask<br/>→ 看到 ******]
end
S1 --> S2 --> S3
S3 --> RESULT[最终:华东区处方数据<br/>无身份证列<br/>姓名为 ******]
class S1,S2,S3 bpProcess
class RESULT bpSuccess
图 18-6 三层协同的实战场景
| 防线 | 防护对象 | 即使被绕过…… |
|---|---|---|
| RLS(行级) | 防止跨区域/租户数据泄露 | CLS 仍限制敏感列 |
| CLS(列级) | 防止敏感字段被未授权访问 | 脱敏 UDF 仍让值不可读 |
| 脱敏 UDF(值级) | 防止看到真实敏感值 | (最后一道防线) |
表 18-7 三层协同的实战场景
引申
三层纵深防御体现了安全架构的"不依赖单一防线"原则。即使某一层配置失误(如 RLS 策略遗漏了某个角色),其他层仍能兜底。这在医药行业尤为重要——GxP 合规要求数据访问可审计、可控制,单一防线无法满足审计要求。
18.4 引申:医药 GxP 数据完整性与中国数据合规(PIPL)¶
GxP 数据完整性¶
医药行业受 GxP(GMP/GCP/GLP)法规约束,要求数据满足 ALCOA+ 原则:
| 原则 | 含义 | 在平台中的实现 |
|---|---|---|
| Attributable | 可归属 | 审计日志记录谁在何时访问了什么 |
| Legible | 可读 | 数据标准化为清晰格式 |
| Contemporaneous | 同步性 | 批次标识记录数据加载时间 |
| Original | 原始性 | Landing 层保留原始数据 |
| Accurate | 准确性 | 质量校验框架保障 |
| + Complete | 完整性 | 行数对账保障 |
| + Consistent | 一致性 | 统一 schema 和口径 |
| + Enduring | 持久性 | S3 生命周期策略保障留存 |
| + Available | 可用性 | 多环境 + 备份保障 |
表 18-8 GxP 数据完整性
这张表在 Ch 1 出现过"法规要求"视角,这里补"工程实现"视角——每项原则在本章跟前序章节怎么落地。"可归属 Attributable"靠审计日志(§18.2.3 DSR 审计报告 + Ch 11 审计日志体系);"原始 Original"靠 Landing 层不可变副本(Ch 7 分层设计);"准确 Accurate"靠 PyDeequ 质量校验(Ch 17 质量框架)。这些不是事后对照,是我设计每一层时就按 ALCOA+ 反向推导——"这个设计满足哪几条 ALCOA?"。第四年 GxP 审计,审计员看到这张"原则→实现"映射表,说"这是我见过最系统的 ALCOA 落地"——每条都能追溯到具体的工程机制,不是空喊"我们重视数据完整性"。合规不是 PPT 里的口号,是嵌在每一层架构里的工程机制(M10 合规从第一天嵌入)。
中国数据合规(PIPL)¶
《个人信息保护法》(PIPL)对个人数据处理的要求:
| PIPL 要求 | 平台应对 |
|---|---|
| 最小必要原则 | 只采集业务必需的个人数据 |
| 目的限制 | 脱敏后的数据不超范围使用 |
| 安全保障义务 | RLS/CLS/脱敏三层防护 + KMS 加密 |
| 数据本地化 | 数据驻留 AWS China(cn-north-1) |
| 可审计 | 审计日志全链路记录 |
表 18-9 中国数据合规(PIPL)
PIPL 这五条要求里,"最小必要"和"目的限制"对架构影响最深——不是"加个脱敏就行",而是要求平台对"每个字段为什么入仓、被谁消费"有明确的声明。我在第一年做 PIPL 合规自查,发现平台里有几个字段"不知道为什么入仓了"——它们是最初批量摄取时"先都收进来再说"的产物,没有明确的消费目的。PIPL 要求"目的限制",这些"无目的字段"就是合规风险。我花了两个月做了"字段级用途标签"治理——每个字段标注"采集目的/消费场景/脱敏策略/保留期限",无标签字段限期下线。这个治理后来成了 Ch 40 语义平面 字段级元数据管理的雏形。PIPL 倒逼了字段级元数据治理——合规驱动架构演进的典型案例(M10)。
Trade-off
合规要求和开发效率天生有张力。比如 GxP 的"可归属"要求每个操作都有审计记录,这加了系统复杂度。但合规不是可选项——医药行业违规的代价(罚款+声誉损失+监管处罚)远超合规成本。架构师得把合规当"非功能性需求"纳入设计,不能事后补丁。我在企业征信时犯过"先建平台后补合规"的错——上线半年后补审计日志,发现全链路无埋点,只能推倒重来。到 Aurora 我从第一天就把 GxP/PIPL 嵌入架构——前期慢一点,但避开了"推倒重来"的代价。合规前置的成本是线性的,合规后补的成本是指数的。
本章小结¶
- 匿名化策略矩阵:mask(完全脱敏)/ partial_mask(部分脱敏)/ aes_encrypt(可逆加密)/ md5(不可逆哈希)/ clear(不处理)——按"是否需分析/还原/关联"选择
- 医药行业字段脱敏决策表把策略选择落到 9 类具体字段(患者标识/患者敏感/医生标识/药品定价等),作为脱敏规则配置依据
- 配置驱动的字段级脱敏:规则在配置中声明,Enriched 加工时由 UDF 执行,不可绕过;aes_encrypt 的密钥来自 KMS 信封加密
- KMS 信封加密:CMK 永不出 KMS,Data Key 仅内存用完即焚,密钥管理与脱敏使用职责分离
- DSR 数据主体权利响应:基于血缘反追 → 分层删除(Redshift 软删 → Enriched 覆盖 → Raw 删 → Landing 保留标记)→ 审计报告
- 脱敏验证:不可逆性检查、格式保留检查、重识别攻击测试,确保脱敏不可绕过
- 三层纵深防御:RLS(行级)+ CLS(列级)+ 脱敏 UDF(值级),不依赖单一防线
- GxP ALCOA+ 原则和 PIPL 合规要求作为非功能性需求纳入架构设计——合规不是可选项
下一章
Ch 19 任务开发配方与实战案例 —— 理论和框架都讲完了,接下来看四个实战 Recipe,把前面的知识串起来。