导出用户数据时如何有效脱敏手机号和邮箱?

迷人的紫瑶 阅读 43

最近在做用户数据导出功能,需要脱敏手机号和邮箱,但实现后发现有些数据没处理好。

我写了这样的脱敏函数:


function anonymizeData(data) {
  return data.map(user => ({
    ...user,
    phone: user.phone.replace(/(d{3})d{4}(d{4})/, "$1****$2"),
    email: user.email.replace(/^(.*?)(@.*)$/, "$1***$2")
  }));
}

测试时发现手机号”13812345678″能变成”138****5678″没问题,但邮箱比如”test@example.com”被处理成”t***@example.com”,但当邮箱是”ab@example.com”时会变成”a***@example.com”,这样第一个字符泄露了?该怎么改才能保证中间至少保留4个星号?

我来解答 赞 7 收藏
二维码
手机扫码查看
2 条解答
端木楠楠
问题出在邮箱的正则匹配逻辑上。当前写法只保证了@前面的部分会被替换,但没控制中间固定长度的星号数量。

关键是要把邮箱用户名部分强制截断处理,保留前1后1字符,中间用4个星号填充:

function anonymizeData(data) {
return data.map(user => ({
...user,
phone: user.phone.replace(/(d{3})d{4}(d{4})/, "$1$2"),
// 邮箱用户名部分保留前1后1,中间用4个星号连接
email: user.email.replace(/^([^@]{0,1})[^@]([^@]{0,1}@.)$/, "$1$2")
}));
}

这个正则表达式的意思:
^[^@]{0,1} 匹配开头最多1个非@字符
[^@] 匹配中间任意数量的非@字符(这部分会被替换)
([^@]{0,1}@.)$ 匹配结尾最多1个非@字符+@符号及后面完整域名

这样处理后:
test@example.com -> tt@example.com
ab@example.com -> ab@example.com
a1b2c3@example.com -> a3@example.com

手机号正则没问题,已经能保证中间4个星号。邮箱这里要特别注意用户名部分必须强制保留前1后1字符,中间统一用4个星号替代,才能避免你遇到的字符泄露问题。
点赞 6
2026-02-05 05:00
小佳鑫
小佳鑫 Lv1
你的正则逻辑没问题,但对长度太短的字段没做校验,导致脱敏不充分。比如邮箱用户名部分如果只有两个字符,那用原来的正则就会泄露信息。

你可以在脱敏前加一个判断,确保用户名部分足够长,再进行替换。否则可以整体替换为 **** 或者保留一个模糊版本。

修改后的函数可以这样写:

function anonymizeData(data) {
return data.map(user => ({
...user,
phone: user.phone ? user.phone.replace(/^(d{3})d{4}(d{4})/, "$1****$2") : '',
email: user.email
? user.email.replace(/^([^@]{4,})@(.*)$/, (match, p1, p2) => {
return p1.length >= 4 ? '*'.repeat(4) + '@' + p2 : '*'.repeat(p1.length) + '@' + p2;
})
: ''
}));
}


这样处理:
- 如果邮箱用户名部分长度 >= 4,就统一替换成四个 *
- 如果长度小于 4,比如是 ab,就替换成对应长度的 *,避免泄露原始长度信息

其实还可以再加一层判断,比如直接对邮箱用户名统一替换为固定长度的星号,例如永远显示 ****@example.com,这样更安全,但具体取决于你的业务要求。

建议:脱敏之前,对字段长度做校验,避免原始信息残留。脱敏的最终目标是防止敏感数据泄露,哪怕是一个字符都不应该暴露。
点赞 6
2026-02-04 15:00