4、脱敏算法详解(上):替换、遮蔽与混洗
脱敏算法,说白了就是给敏感数据“换脸”的技术。你想想看,数据湖里存着各种用户信息——姓名、手机号、邮箱、身份证号……这些数据如果直接暴露给开发测试环境,风险太大了。
我个人习惯把脱敏算法分成三大类:替换、遮蔽和混洗。今天咱们先聊这三类。它们各有各的适用场景,也各有各的坑。
4.1 替换算法:给敏感数据“换个名字”
替换算法,就是用预设的假数据,把真实数据替换掉。比如把“张三”换成“李四”,把“zhangsan@example.com”换成“test@test.com”。
我在项目中遇到过,很多团队觉得替换算法最简单,直接上。结果呢?替换后的数据失去了业务关联性,测试用例跑不通。嗯,这里要注意:替换算法不是简单的“找本字典随机替换”。
4.1.1 姓名替换
姓名替换,常见做法是维护一个“假名库”。比如从百家姓里随机取姓,再从常用名字库里随机取名。
// 伪代码示例:姓名替换
function replaceName(realName) {
// 从假名库中随机选取
const fakeSurname = ['张', '李', '王', '赵', '刘'][randomIndex];
const fakeGivenName = ['伟', '芳', '娜', '秀英', '敏'][randomIndex];
return fakeSurname + fakeGivenName;
}
4.1.2 邮箱替换
邮箱替换,不能只替换@前面的部分。我见过有人只把用户名替换成随机字符串,邮箱域名保持不变。结果呢?攻击者通过域名就能反推用户所属公司。
正确的做法是:用户名和域名都替换。而且,替换后的邮箱要符合邮箱格式规范。
// 伪代码示例:邮箱替换
function replaceEmail(realEmail) {
const fakeUser = 'user' + randomString(8);
const fakeDomain = ['test.com', 'example.org', 'demo.net'][randomIndex];
return fakeUser + '@' + fakeDomain;
}
4.2 遮蔽算法:只让你看到“该看的部分”
遮蔽算法,就是把敏感数据的部分字符用特殊符号(比如*)代替。你想想看,客服系统里,客服只需要看到用户手机号的前三位和后四位,中间四位用*号遮蔽,就足够了。
遮蔽算法的核心是:保留业务所需的最小信息量。
4.2.1 手机号中间四位遮蔽
这是最常见的遮蔽场景。手机号 11 位,前三位是运营商号段,后四位是用户编号。中间四位遮蔽后,既能识别用户,又不会泄露完整号码。
// 伪代码示例:手机号遮蔽
function maskPhone(phone) {
// 保留前3位和后4位,中间用****代替
return phone.substring(0, 3) + '****' + phone.substring(7);
}
// 输入:13812345678
// 输出:138****5678
4.2.2 其他常见遮蔽场景
| 数据类型 | 遮蔽规则 | 示例 |
|---|---|---|
| 身份证号 | 保留前6位和后4位,中间遮蔽 | 110101******1234 |
| 银行卡号 | 保留后4位,其余遮蔽 | **** **** **** 5678 |
| IP地址 | 遮蔽最后一段 | 192.168.1.*** |
| 地址信息 | 保留到街道/乡镇级别,遮蔽具体门牌号 | 北京市海淀区*** |
4.3 混洗算法:打乱数据,但保持统计特征
混洗算法,就是把同一列的数据随机打乱顺序。比如,有一列“年龄”数据,混洗后,每个人的年龄还是原来的那些值,只是分配给了不同的人。
混洗算法的好处是:保持了数据的统计分布特征。比如年龄的均值、方差、最大值、最小值,混洗前后完全一致。这对于数据分析场景非常有用。
4.3.1 混洗的实现方式
混洗的核心是“随机置换”。但要注意:不能简单地对整列进行随机排序。为什么呢?因为如果多列数据之间存在关联关系(比如“年龄”和“收入”),单独混洗某一列会破坏这种关联。
// 伪代码示例:单列混洗
function shuffleColumn(dataArray) {
// Fisher-Yates 洗牌算法
for (let i = dataArray.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[dataArray[i], dataArray[j]] = [dataArray[j], dataArray[i]];
}
return dataArray;
}
4.3.2 混洗 vs 替换:怎么选?
| 对比维度 | 混洗算法 | 替换算法 |
|---|---|---|
| 数据分布 | 保持原始分布 | 可能改变分布 |
| 关联关系 | 破坏列间关联 | 可保持列间关联(通过映射表) |
| 可逆性 | 不可逆(除非记录映射) | 可逆(通过映射表) |
| 适用场景 | 数据分析、机器学习 | 功能测试、开发环境 |
4.4 三种算法的选择策略
说了这么多,到底怎么选?我个人的经验是:
- 需要保持数据原始格式和业务含义 → 用替换算法。比如测试环境需要“看起来像真实数据”的姓名和邮箱。
- 只需要部分信息,且要保留识别能力 → 用遮蔽算法。比如客服系统、日志脱敏。
- 需要保持数据统计特征,用于分析 → 用混洗算法。比如数据湖里的分析数据集。
当然,实际项目中往往是组合使用。比如:姓名用替换,手机号用遮蔽,年龄用混洗。嗯,这里没有标准答案,只有最适合你场景的方案。