第3章:隐私增强SDK的架构设计
聊完了策略的基础概念,我们得看看实际工程里这些东西怎么落地。说白了,一个靠谱的隐私增强SDK,内部架构绝不是一个“黑盒子”。你需要把每一层都设计清楚,才能保证数据在“防泄露”和“业务可用”之间找到平衡。
我在做第一款企业级SDK时,最头疼的就是模块之间互相“打架”。数据采集模块刚收上来的信息,策略引擎还没审核完,就直接被发送模块丢出去了。嗯,那场面…后来我们彻底重构了架构,才有了下面这套大家公认的“标准分层”。
数据采集层——入口处的“安检”
这里负责搜集业务方传递过来的原始数据。你想想看,这就像机场登机前的安检机。我见过不少团队在这一层几乎不加控制,啥数据都往里塞,结果后面的策略引擎直接被撑爆,处理延迟飙升。
- 接入方式:支持同步API和异步广播两种模式。同步适合高频、小额的数据点;异步适合批量日志这种。
- 预处理策略:我习惯在这里做第一轮格式校验。比如乱码、超长字段,直接丢弃,不给后面的策略引擎添堵。
- 数据标记:这一层会打上“采集时间戳”“来源App ID”“数据分级(如隐私/非隐私)”。
策略引擎层——决策的核心大脑
这一层是整个SDK的“CPU”。所有数据流到这里,必须过一遍策略规则。说白了,引擎要回答四个问题:能不能用?该不该脱敏?脱到什么程度?要不要阻断?
规则解析器
我比较推荐用DSL(领域特定语言)来写规则,而不是硬编码。你看很多开源SDK,后来扩展新策略都得重新发版,累死。用DSL,可以直接从云端热加载规则。
# 示例:DSL策略规则片段
rule "ip_filter"
when
source == "user_info" and field.name == "ip_address"
then
action = "mask"
params.keep_octets = 2
end
rule "phone_masking"
when
field.category == "phone_number"
then
action = "truncate"
params.expose_last_4 = true
end
决策树执行
| 输入特征 | 决策节点 | 执行结果 |
|---|---|---|
| 字段 = 身份证号 | 隐私等级判定(L3) | 阻断输出(高危) |
| 字段 = 脱敏后的姓名 | 是否已经过脱敏处理 | 允许输出(二次脱敏) |
| 来源 = 外部第三方SDK | 来源信任度评分 | 额外加固处理后输出 |
在实际工程里,决策树可以是二叉树,也可以是多叉树。我个人偏向用一组轻量级的状态机,每个状态对应一个策略集合。这样做的好处是:当策略冲突时,状态机能按优先级顺序裁决。
输出过滤层——数据出境的最后一道门
数据经过策略引擎判定后,可能会被打上“需要脱敏”或“需要封装”的标记。过滤层做的就是制裁执行。
- 脱敏模组:支持多种“预置脱敏器” —— 手机号、邮箱、IP、银行卡、身份证,每种都有专门的脱敏算法。
- 字段裁剪:对于高频日志,如果某个字段判定为“非必要”,直接摘掉。我见过一个案例,某日志字段列表里有20多个字段,实际业务只需要3个,剩下全是冗余私密信息。
- 实时拦截:对于判定为“严重违规”的数据,直接返回空结果,并触发错误码。
// 输出过滤示例 — 脱敏器注册表(Java风格)
MaskerRegistry registry = new MaskerRegistry();
registry.register("phone", new PhoneMasker(MaskLevel.EXPOSE_LAST_4));
registry.register("email", new EmailMasker(MaskLevel.DOMAIN_ONLY));
// 执行过滤
OutputFilter filter = new OutputFilter(registry);
SafeOutput result = filter.apply(rawData, policyDecision);
审计与溯源模块——事后追责与复盘
很多人都觉得审计模块就是记个日志,毫无挑战。说实话,我也是这么想的,直到有一次生产环境出了数据泄露问题,追查时发现日志只有“数据已发送”几个字,啥细节都没有。我们被迫把所有应用服务停机,逐行反编译代码才找到问题点。
从那次之后,我对审计模块有了执念。它的职责有三条:
- 全链路记录:每一条数据的“来龙去脉”都要留下痕迹。谁采集的?经过了哪些策略?最终输出了什么?
- 脱敏复核:审计模块会随机抽样一些输出数据,做一次“回放检查”——如果用相同的策略再跑一遍,结果是否一致?如果不一致,说明策略被篡改或引擎状态异常。
- 告警与通知:当检测到异常模式(比如某条策略命中率突然从1%飙升到90%),立刻触发运维告警。
| 审计条目 | 记录内容示例 | 用途 |
|---|---|---|
| RequestID | UUID-2024-xxxx | 跨模块关联 |
| 原始字段 | phone: 138****0000(已遮蔽) | 脱敏前快照 |
| 应用策略 | phone_mask_rule_v3 | 策略版本溯源 |
| 输出结果 | 138****0000 | 脱敏后最终内容 |
| 时间戳 | 2024-06-15T10:30:00Z | 精确到毫秒 |
各模块如何协同工作?
上面讲了每一层的职责,但实际运行时,它们是一套紧密配合的流水线。我梳理了一下典型的协同流程,你用这个例子来理解会清晰很多:
假设业务方要上报一条用户注册日志。这条数据包含:用户名、手机号、IP地址、设备型号。我们不希望把手机号和IP原样暴露出去。运作流程如下:
- 数据采集层收到原始JSON,校验格式无误,打上“来源:App_A”“数据分级:L2(一般含个人标记)”。
- 策略引擎层接收到后,执行本地规则集。命中
phone_mask_rule和ip_mask_rule,输出决策书:“将phone字段做脱敏,IP段保留后两位”。 - 输出过滤层从决策书中读取指令,调用脱敏器处理 phone 和 IP。结果写入一个新的“干净”JSON对象。
- 审计模块在过滤层处理完成后,截获原始JSON、决策书、干净JSON,打包成一个审计事件,异步写入日志系统。
- 最终,输出过滤层向业务方返回干净的JSON。
你可能会问:为什么不把审计提前?我告诉你,审计放最后是有道理的。因为只有在最终输出确定后,你才能完整记录“究竟什么被输出、什么被阻止了”。
好了,这就是SDK架构设计的核心骨架。这套架构我在好几个项目中都用了,可扩展性强,模块之间解耦得也比较干净。你在实际落地时,可以根据业务量做裁剪,比如采集量不大的话,采集层可以简化。