文章目录
前言:随着信息化发展与数字转型,企业有大量的数据二次利用(secondaryuse)与挖掘需求,为了平衡数据利用与隐私保护(敏感数据保护)问题,数据脱敏——作为一种成熟且应用灵活的数据安全技术,成为当前绝大数企业在数据安全治理与建设过程中的必选技术与措施。
一、简单逻辑的数据脱敏处理
在前端项目开发中,对于一些敏感信息,如:身份证号,手机号,用户名等,是不希望他人看到的。
一般处理的方式是使用 “*” 代替中间字符,这就是“脱敏法”。
数字化管理平台
Vue3+Vite+VueRouter+Pinia+Axios+ElementPlus教程
权限系统-商城
个人博客地址
1.1 手机号脱敏
我国使用的号码编码为11位,其中前3位是网络识别码,表示是联通,移动,电信;第4-7位是地区编码;第8-11位是用户号码(随机分配)。如图2所示。根据手机号码可推断出号码用户的号码归属地、以及运营商选择信息。除去手机号的第一位默认为1,其他位的范围一般都可取0-9,那么可估计最大生成的号码规模为10^10= 100亿。
phone.replace(/^(.{3})(?:\d+)(.{4})$/, "$1****$2")
1.2 身份证号脱敏
我国公民身份号码为18位,包含标识主体丰富的时空信息。它可表示为图1所示。由17位数字本体码和1位校验码组成。前6位是地址码编码:1-2位表示省(自治区、直辖市、特别行政区)、3-4位表示市(地区、自治州、盟及国家直辖市所属市辖区和县的汇总码)、5-6表示县(市辖区、县级市、旗)。而第7-14位数字表示出生日期,包括4位出生年份+4位生日日期;15-17位数字顺序码(其中17位的奇数分给男性,偶数分给女性),18位是数字校验码,可由确定的校验公式计算得到。
由上图可知,给定一个公民身份证号码,可推断出该公民相关的出生地、出生年月和性别的信息。对于共享同一种地址码的人口,可以由国内各个地区的公开人口统计得到,这个数据量与地区人口密度相关,比如东部密度大,该信息具有很弱的“身份可识别性”,而西部一些偏远地区密度小,该信息可能具有很高的“身份可识别性”。对于同一个出生日期的人口数量,为了简单估计,假设为均匀分布,年龄在0-130范围,那么同一天出生大约为3万人(14亿/(130*365))。由此可见,单单暴露身份证号码的出生日期8位,“身份可识别性”很弱。然而,将地区编码考虑进来,3万种可能性进一步消除,再经过1000种可能性的顺序码(15-17位),可完全消除多种可能性,“身份可识别性”达到唯一水平。
idCard.replace(/^(.{6})(?:\d+)(.{2})$/, "$1**********$2")
1.3 代码演示
columns: [
{ dataIndex: 'shipperName', title: '企业名称', width: 230 },
{ dataIndex: 'serialNumber', title: '承运单号', width: 260 },
{ dataIndex: 'contractTypeName', title: '订单类型', width: 100, align: 'center' },
{ dataIndex: 'driverName', title: '司机姓名', width: 100, align: 'center' },
{ dataIndex: 'driverPhone',
title: '司机手机号',
width: 130,
align: 'center',
customRender: (text) => {
return text.replace(/^(.{3})(?:\d+)(.{4})$/, '$1****$2')
} },
{ dataIndex: 'plateNumber', title: '车牌号', width: 110, align: 'center' },
{ dataIndex: 'contractTime', title: '接单时间', width: 170, align: 'center' }
]
二、复杂逻辑的数据脱敏处理
2.1 用户名脱敏
用户名不同于手机号和身份证号,它属于指定范围内的不定长度,可能是两个字、三个字、四个字等等,所以需要通过判断的方式来处理。
const nameMask = (name) => {
let result = ""
switch (true) {
case name.length === 2:
return name.substring(0, 1) + '*'
break;
case name.length === 3:
return name.substring(0, 1) + "*" + name.substring(name.length-1)
break;
case name.length >= 4:
return name.replace(/^(.{1})(?:.{1,})(.{1})$/g, "$1**$2")
break;
}
return result;
}
注:具体的数据处理规则,请根据业务需求适当调整。
三、解释说明
3.1 正则表达式中的 ()
正则表达式中的 () 就是起到一个分组作用,将匹配到的放到 mathches 集合中,$ 相当于集合名字,1-9 就相当于索引,$1…$9 相当于对应索引的值。注意下标是从 1 开始,表示第一个元素,不是从0 开始。
3.2 涉及到的正则相关知识
1. 匹配模式
表达式 | 描述 |
---|---|
[abc] | 查找方括号之间的任何字符。 |
[^abc] | 查找任何不在方括号之间的字符。 |
[0-9] | 查找任何从 0 至 9 的数字。 |
[a-z] | 查找任何从小写 a 到小写 z 的字符。 |
[A-Z] | 查找任何从大写 A 到大写 Z 的字符。 |
[A-z] | 查找任何从大写 A 到小写 z 的字符。 |
[adgk] | 查找给定集合内的任何字符。 |
[^adgk] | 查找给定集合外的任何字符。 |
(red|blue|green) | 查找任何指定的选项。 |
注意:[A-z] 这里有六个特殊的字符存在于 Z 到 a之间,所以严格意义上讲,它并不能真正的匹配所有英文字母,因为还有可能匹配到特殊字符。
2. 量词
量词 | 描述 |
---|---|
n+ | 匹配任何包含至少一个 n 的字符串。例如,/a+/ 匹配 “candy” 中的 “a”,“caaaaaaandy” 中所有的 “a”。 |
n* | 匹配任何包含零个或多个 n 的字符串。例如,/bo*/ 匹配 “A ghost booooed” 中的 “boooo”,“A bird warbled” 中的 “b”,但是不匹配 “A goat grunted”。 |
n? | 匹配任何包含零个或一个 n 的字符串。例如,/e?le?/ 匹配 “angel” 中的 “el”,“angle” 中的 “le”。 |
n{X} | 匹配包含 X 个 n 的序列的字符串。例如,/a{2}/ 不匹配 “candy,” 中的 “a”,但是匹配 “caandy,” 中的两个 “a”,且匹配 “caaandy.” 中的前两个 “a”。 |
n{X,} | X 是一个正整数。前面的模式 n 连续出现至少 X 次时匹配。例如,/a{2,}/ 不匹配 “candy” 中的 “a”,但是匹配 “caandy” 和 “caaaaaaandy.” 中所有的 “a”。 |
n{X,Y} | X 和 Y 为正整数。前面的模式 n 连续出现至少 X 次,至多 Y 次时匹配。例如,/a{1,3}/ 不匹配 “cndy”,匹配 “candy,” 中的 “a”,“caandy,” 中的两个 “a”,匹配 “caaaaaaandy” 中的前面三个 “a”。注意,当匹配 “caaaaaaandy” 时,即使原始字符串拥有更多的 “a”,匹配项也是 “aaa”。 |
n$ | 匹配任何结尾为 n 的字符串。 |
^n | 匹配任何开头为 n 的字符串。 |
?=n | 匹配任何其后紧接指定字符串 n 的字符串。 |
?!n | 匹配任何其后没有紧接指定字符串 n 的字符串。 |