常用脱敏算法(二):泛化、扰动、加密脱敏
聊完了替换和遮蔽,咱们接着看剩下的三个杀手锏:泛化、扰动、加密。说实话,这三种方法在实际项目里用的频率更高,因为它们能保住数据的“统计意义”或者“可用性”。
我个人习惯把脱敏分成两类:可逆和不可逆。泛化和扰动是不可逆的,加密是可逆的。这决定了你后面怎么用这些数据。
1. 泛化:把细节磨平
泛化说白了,就是把精确值变成一个范围或一个模糊概念。比如具体年龄改成年龄段,具体地址改成城市或省份。
举个例子:
- 原始:张三,28岁,北京市朝阳区望京街道
- 泛化后:张三,20-30岁,北京市朝阳区
你想想看,这个数据做人群分析还能用,但不能定位到具体哪个人。这就是泛化的核心价值——保留统计分布,丢掉个体细节。
我记得有一次做金融风控模型,需要客户年龄分布。原始数据里年龄字段特别精确,但模型只关心年龄段。当时我就用泛化把年龄映射成 [18-25, 26-35, 36-45, 46-55, 56+] 这五个区间,既合规又够用。
| 字段 | 原始粒度 | 泛化粒度 |
|---|---|---|
| 出生日期 | 1990-03-15 | 1990年 |
| 手机号 | 138****1234 | 138****0000 |
| 地理坐标 | (39.9042, 116.4074) | 城市级(北京) |
| 收入 | ¥18,500 | ¥15,000-¥20,000 |
2. 扰动:加点噪声,改变事实
泛化是精度的牺牲,扰动是准确性的牺牲。扰动通过给原始数据加上随机噪声,让数据在宏观上还能保持分布,但微观上已经不对了。
最常见的做法是加拉普拉斯噪声,这玩意儿和差分隐私是绑定在一起的。为什么用拉普拉斯分布?因为它的数学性质能保证隐私预算可控,具体公式我就不推了,咱们直接看代码。
// Java 示例:对年龄字段加拉普拉斯噪声
public double addLaplaceNoise(double originalValue, double epsilon, double sensitivity) {
Random rng = new Random();
double beta = sensitivity / epsilon; // 尺度参数
double u = rng.nextDouble() - 0.5;
return originalValue - beta * Math.signum(u) * Math.log(1 - 2 * Math.abs(u));
}
// 调用示例:ε=1.0,敏感度=5(年龄范围5年)
double noisyAge = addLaplaceNoise(28.0, 1.0, 5.0);
// 输出大概在 26.5 ~ 29.5 之间浮动
你看,输出的年龄看着像真的,但其实不是真的。也就是说,攻击者无法确定真实年龄到底是28还是29,这就达到了保护目的。
不过,这里有个大坑:噪声加多了,数据就废了。我曾经在一个医疗项目里,一开始把ε设成0.1,结果加完噪声,平均值偏移了20%,模型直接崩了。后来调成ε=1.0,才勉强能用。所以ε这个参数,真得试。
3. 加密脱敏:安全,但有成本
泛化和扰动都是不可逆的,但有些场景你必须能还原数据。这时候就得用加密脱敏。
常用加密方式有几种,我列个表对比一下:
| 加密方式 | 可逆性 | 性能 | 典型用途 |
|---|---|---|---|
| AES-256(对称) | 可逆 | 快 | 字段级加密,如身份证号 |
| RSA(非对称) | 可逆 | 慢 | 密钥交换、小数据加密 |
| SHA-256(哈希) | 不可逆 | 快 | 密码存储、比对 |
| HMAC(带密钥哈希) | 不可逆 | 快 | 、签名 |
这里我要专门提一下格式保持加密(FPE)。你想想看,如果你的数据库里身份证号是18位,加密后变成32位乱码,那数据库字段就得重构,下游系统全得改。FPE就是解决这个问题的——加密后的长度和格式和原来一模一样。
4. 三种算法怎么选?一张图说清楚
下面这张SVG图,我画了这三种算法各自的核心流程和应用场景。你一看就知道什么情况选什么。
看完这张图,你是不是心里有数了?这三种算法没有的好坏,关键看场景。泛化牺牲精度,扰动牺牲准确性,加密牺牲性能。搞清楚你的业务到底需要什么,再决定选谁。
5. 小结一下我的三条原则
- 能不加密就不加密:加密带来密钥管理成本,而且影响查询性能。能用泛化或扰动解决,就别碰加密。
- 扰动之前先测模型:噪声参数ε太小会让数据失真,强烈建议先在小样本上跑一遍,看看分布变化。
- 时刻想着“逆不逆”:如果你只是分析用,不可逆就行了,别图方便用可逆加密,那是给自己找麻烦。