背景
随着信息社会高速发展,大家对隐私数据的安全越来越重视,现在市面上各种搜集个人信息的网站,app层出不穷,你也不知道你的信息提交上去后,提供服务的那边,是不是会妥善保管好你的个人数据,包括但不限于,身份证号,手机号,各种卡号等等。
不久前诈骗犯猖獗的时候,绝大部分就是因为你的隐私数据泄露,造成的安全风险。所以作为用户和消费者,遇到平时填写个人信息的场景,一定要慎重。
那作为负责任的服务提供商,也应该做的妥善保管用户的隐私信息,这是互信的基础。
法律法规
当然,到今天为止,隐私数据的保护,不仅仅是企业责任,更是法律要求,目前我国出台的涉及到个人隐私信息保护的法律包括但不限于:
- 《中华人民共和国个人信息保护法》:这部法律于2021年11月1日正式实施,是我国第一部专门针对个人信息保护的法律,旨在保护个人信息权益,规范个人信息处理活动,保障个人隐私不受侵犯。
- 《中华人民共和国民法典》:在人格权编中有明确规定隐私权和个人信息保护的内容,尤其是在第四编第六章中详细描述了隐私权和个人信息保护的相关规定。
- 《中华人民共和国网络安全法》:该法于2017年6月1日开始实施,是关于网络安全的基础性法律,对于个人信息的安全保护也有所涉及。
- 《中华人民共和国数据安全法》:此法进一步强化了数据安全管理,保护数据安全,促进数据开发利用。
所以,作为系统服务的提供者,做好用户个人隐私数据的保护责无旁贷。
保护形式
数据脱敏处理,一般来说有2种形式。
不可逆脱敏
当用户提交个人信息后,筛选出其敏感字段,如身份证号码,手机号,银行卡号等,进行不可逆的加密,比如截断前几位和后几位,中间部分直接替成星号之类的符号,也可以直接把整个字段用一些摘要算法生成哈希值,反正是不可逆的,怎么简单怎么来。
这样做虽然简单,高效,但劣势也很明显,当我们真的需要联系,或者在某些紧急情况需要查看用户信息时,就不好处理了。
可逆脱敏
可逆的方式就是再保存信息是,多一步操作,将用户提交的敏感信息,进行加密,加密方式可以根据实际情况,采用对称或者非对称加密方式,总之是有办法查看原始数据的。
*可逆还安全吗?
既然可逆,那还安全吗?
当然是安全的,首先,用户的数据是以安全的方式进行存储的,即便是采用可逆脱敏的方式,解密的密钥一定是由用户自己保管。而且,即使要执行查看敏感数据的操作,也是需要一定的权限或者授权操作,比如发送验证码等。不是谁想看就可以看的,当然要做到这点也是需要企业遵守契约精神。
那如果有企业说,既然能解密,我不想那么麻烦,还要等验证码,还要用户授权,还要经过怎么怎么样的流程,太耽误时间了,一把梭直接明文不是更好。请参考上面👆提到的几部法律,一旦被查实,罚款通报都是从轻发落了。
总之,涉及用户敏感信息的环节,进行数据脱敏的操作,不是自寻烦恼,而是必要的保护性措施,即便是增加了一定的管理复杂度,也是必须要付出的代价。
其实还有一种脱敏方式,就是伪脱敏,简单来说就是,用户数据什么的都是明文一把梭,只在UI层做了点字符替换之类的操作,糊弄用户。这种行为,要么是不会,要么就是懒,无它。
脱敏流程
站在整个系统角度,脱敏流程大概如下图所示,在系统中流转的数据,一旦涉及到敏感字段,都需要经过脱敏操作,才能进行后续业务。
而单独看脱敏操作这个环节,又可以分为几种情况
- 如果操作管理系统的是系统级管理员,拥有很高的管理权限,也就有查看用户敏感信息的权限,这种,可以根据实际情况,放宽查看用户解密密钥的权限。获取到解密密钥后,可以查看用户敏感数据。当然所有的操作必须要有日志记录。
- 如果是普通管理人员,只负责某些业务,有需要用到用户敏感信息的情况,这种一般的处理结果是做到“可用不可见”的效果,比如服务提供方需要给用户发送短信,就必须要知道用户的手机号码,而服务端是可以识别这种场景,在服务端完成解密敏感信息,并发送短信的动作,而无需把手机号展示在ui层。
- 如果是用户自己在用户端系统上查看个人敏感数据,一般完成基本的认证就可以了,比如验证码,人脸识别等手段都是ok的。
总结起来,流程基本如下
样例代码
我这里是在模型层创建了一个中间层,专门处理敏感数据
public class MaskDataDto()
{
public string text { get; set; }
public MaskDataType maskDataType { get; set; }
public uint firstPoint
{
get
{
if (maskDataType == MaskDataType.ChinaCellPhone)
return 3;
if (maskDataType == MaskDataType.ChinaIdCard)
return 4;
return Convert.ToUInt32(Math.Ceiling(Convert.ToDouble(text.Length) / 3));
}
set { }
}
public uint lastPoint {
get
{
if (maskDataType == MaskDataType.ChinaCellPhone)
return 7;
if (maskDataType == MaskDataType.ChinaIdCard)
return 14;
return Convert.ToUInt32(Math.Ceiling(Convert.ToDouble(text.Length) / 1.5));
}
set { }
}
//用户专属id
public string keyId { get; set; }
//用户专属secret
public string keySecret { get; set; }
public bool valid
{
get
{
if (string.IsNullOrEmpty(keyId) || string.IsNullOrEmpty(keySecret))
return false;
if (maskDataType == MaskDataType.ChinaCellPhone && text.Length != 11)
return false;
if (maskDataType == MaskDataType.ChinaIdCard && !Assistant.IdCardValidator.IsValidIdCard(text))
return false;
if (firstPoint>=text.Length || firstPoint>lastPoint || lastPoint>=text.Length || lastPoint==0)
return false;
return true;
}
}
public string encryptText
{
get
{
//解码的时候,要先转回普通字符,在解码
return Assistant.Utils.ToBase64Str(Assistant.Security.Encrypt(text, Encoding.UTF8.GetBytes(keyId), Encoding.UTF8.GetBytes(keySecret)));
}
//get;set;
}
// public string[]? splitTexts { get; set; }
public string[]? splitTexts
{
get
{
var textParts = new List<string>()
{
text.Substring(0, (int)firstPoint),
text.Substring((int)lastPoint, text.Length - (int)lastPoint)
};
return textParts.ToArray();
}
}
public string hashText
{
get
{
return Security.GenerateMD5Hash(text);
}
}
}
public IActionResult TestMaskData(string text)
{
var maskData = new MaskDataDto()
{
text = text,
keyId = Utils.GenerateRandomCodePro(16),
keySecret = Utils.GenerateRandomCodePro(16),
maskDataType = MaskDataType.Other
};
Console.WriteLine($"TestMaskData: {JsonHelper.JsonSerialize(maskData)}");
return Ok();
}
代码在实例化阶段,就会分别返回密文,哈希和一个分段的数组。密文就是加密后的敏感数据,哈希值是为了方便检索操作,接受的数据可以直接是哈希值,解决了传输层的安全问题,至于分段的数组,是保存的敏感数据的前缀和后缀,在需要显示敏感字段的地方,返回前缀,后缀以及遮掩字符拼接的安全信息,也能方便检索。
效果
简单看几个脱敏成果
-
控制台打印的服务端脱敏效果
-
存储效果
-
页面效果
-
传输效果
-
高级管理员的解密效果
这里有一些权限识别,动作识别之类动作的无法展示,只能大概看一下几个场景的截图。
好了,基本就是这样了。