乐于分享
好东西不私藏

敏感数据加密和单独存储的补充说明

敏感数据加密和单独存储的补充说明

上一篇是关于敏感信息的加密和单独存储(如何落实敏感信息“加密”和“分开存储”的合规要求?),有网友认为有帮助,并建议再深入探讨下。
这篇文章可能会超过通常合规性的探讨,具体是在上一篇文章的基础上再深入探讨敏感信息加密保存的实现方式。有部分技术贴的内容,对于合规人员,不需要看过程,只需要看结论就可以了
一、身份证和电话是不是敏感信息
上一篇文章中,说到身份证和电话不属于敏感信息,内容以明文方式显示,引起部分疑惑。先说明下这个问题,即身份证和电话是否属于敏感信息。
虽然电话属于“私密”信息,很多人被骚扰都是因为电话被泄露。但是电话一开始就没有纳入到敏感信息中,因为电话还不足以对财产和人身安全带来影响,无非就是不胜其烦而已。
而身份证号码在之前的GB/T35273-2020《信息安全技术 个人信息安全规范》中被定义为了敏感个人信息:
但是,TC260-PG-20244A《网络安全标准实践指南 —敏感个人信息识别指南》以及GB/T45574—2025 《数据安全技术 敏感个人信息处理安全要求》将上述证件信息都排除在敏感信息之外了。
前者的附表名称叫做“常见敏感个人信息”,后者直接叫做“敏感个人信息类别”,排除了“常见”。这种明确定义的方式,避免了敏感信息的模糊性。
虽然从法规要求上没有列为敏感信息,但是手机、证件、地址等信息还是应该在数据分级中列为更高级别,做相应的特殊处理。
二、数据库字段的加密
上一篇文章中说到数据库不支持字段的加密,实际上只是我自己没有用过。本文做一些纠正。
以下是Sqlserver的加密和加密函数(MYSQL等数据库都支持,只是函数名不同):
数据库支持字段加密:
SELECT EncryptByPassPhrase('123456','110101199001011234'as 加密字段
结果是
加密字段0x010000009687C00BBE0A97F3DA8DE68102FDDF588853FB3FC6812F6C9318EFB7EE83FD50B2CAEC985C8FBB69

对上述字段进行解密:

SELECT CONVERT(VARCHAR(50), DecryptByPassPhrase('123456', 0x01000000BA39A9D74D86B306E8D42E204FF369C26EB89EC78A7F83F55B0792898BF2F94A52679E74223906B8)) as 身份证

结果是

身份证110101199001011234

我们再来看下具体的数据表,简单的数据表结构如下:

id	int	Uncheckedname	nchar(10)	Checkedtel	varbinary(100)	Checkedaddr	varbinary(100)	Checked

插入两条数据,保存时对数据进行加密:

insert intoa_test_no (name,tel,addr)values('张三',EncryptByPassPhrase('123456','138022'), EncryptByPassPhrase('123456','深圳福田区'))insert into a_test_no (name,tel,addr)values('李四',EncryptByPassPhrase('123456','138023'), EncryptByPassPhrase('123456''深圳南山区'))

显示插入的数据:

select * from a_test_no

结果如下,数据都是加密的:

id	name	tel	addr1	张三        	0x010000000F1FAC04E0FA9AE37DF8DF2D97C4AB260727E6006BADAAF7	0x010000000F369C5716FE4AFB5C3D6E072BFC648C439A326D6E4D95D5D6C50448942B2CFF2	李四        	0x0100000023C75DC26E8F9B4E3D4E742F71F41A66540FAE0E98F6F6F8	0x01000000E4EA627A99FF8CF716DE5C89A5C3F5EFA145A44A112C08390AA59891B0E3D764

解密查询:

select  id, name, convert(varchar(20), DecryptByPassPhrase('123456',tel)) as tel, convert(varchar(50), DecryptByPassPhrase('123456',addr)) as addr from a_test_no

结果如下:

id	name	tel	addr1	张三        	138022	深圳福田区2	李四        	138023	深圳南山区

这样就实现了字段的加密,在数据库的存储中是密文。

但是,这种加密方式存在以下问题:

1、查询和排序都需要通过解密的方式来处理,一旦数据表容量大,计算的消耗会非常大,可能会非常慢;批量查询、列表分页、查几十上百条每条都要逐行解密运算,CPU 直接损耗 20%~40% 很正常。全表扫描、导出报表、批量批量遍历性能掉得很明显,慢一倍都有可能

2、数据字段的定义必须要定义为二进制方式,并且要进行一次转换,这增加了程序的复杂性,开发过程非常不直观;

3、密码在SQL中以“裸露”的方式显示;

4、加密后的数据会大2-3倍。

上面这种方式加密方式,看起来比上篇文章中那种加哈希的方式要简洁,但是排序和查询会存在很大的问题。所以,字段加密可以采用这种方式,但是加上哈希字段会大幅提升查询和排序的速度。

三、Always Encrypted
上述字段加密的方式在程序处理上实际上非常麻烦,需要从设计到编码上都非常小心,调试和测试都非常别扭。
也正因如此,微软的SQL Server提供了一种数据库级别的加密方式,能极其方便得解决敏感信息加密的问题。
下面我们来给出一个示例。

创建一个加密字段的数据表:

CREATE TABLE a_test1(    ID INT PRIMARY KEY IDENTITY(1,1),    UserName NVARCHAR(50),    IDCard VARCHAR(20)  COLLATE Chinese_PRC_BIN2    ENCRYPTED WITH (        COLUMN_ENCRYPTION_KEY = CEK_AlwaysEncrypted,        ENCRYPTION_TYPE = Deterministic,        ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256'    ),    Phone VARCHAR(20)    COLLATE Chinese_PRC_BIN2    ENCRYPTED WITH (        COLUMN_ENCRYPTION_KEY = CEK_AlwaysEncrypted,        ENCRYPTION_TYPE = Randomized,        ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256'    ),    BankCard VARCHAR(30)    COLLATE Chinese_PRC_BIN2    ENCRYPTED WITH (        COLUMN_ENCRYPTION_KEY = CEK_AlwaysEncrypted,        ENCRYPTION_TYPE = Randomized,        ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256'    ));

表结构如下:

UserName	nvarchar(50)	CheckedIDCard	varchar(20)	CheckedPhone	varchar(20)	CheckedBankCard	varchar(30)	Checked

看起来跟正常表没有什么两样,其中的加密字段也不需要设置为二进制方式。

插入一条数据:

INSERT INTO a_test1 (UserName, IDCard, Phone, BankCard)VALUES ('张三''110101199001011234',  '13800138000',  '6222021234567890123')

在没有加密登录的情形下的查询:

select * from a_test1

结果如下:

ID	UserName	IDCard	Phone	BankCard1	张三	0x014F77A6849DFCFE0CDB95E94739735C602CBBA07140447FDD56C1EBDBC7D6C8C52BF69961C559C1AC1319E8DB9E6CEE30699F04E9A11886CB3C1EFFBB59B466D48D742B3882EF07847AAEDA31284F6598	0x01E0960A3D8121C7152F483576B9BEE5C8C99194C4A41C8ABC491CE3734916272EAEE8C0050A3C3C8FE6561CC200099647FEFCACB4581B267B35561D7B21E69D4B	0x01F069FE07F18513E49A73D5CEBDC6779F922F118A85A9BBFFF6AD5EAA302A2E1E9F4F0E151FC3CC7C92EF835F0C79CA92B4FDD52107D22DBF52DC97AB7DE40B15D74F56A194FE4B0F87EA8E1BA887E742

敏感字段的数据全部是密文。

但是如果以列加密的方式连接数据库,

再执行上面的查询语句,结果如下:

ID	UserName	IDCard	Phone	BankCard1	张三	110101199001011234138001380006222021234567890123

敏感字段就会以明文的方式显示出来。

上述加密方式意味着:

1、数据表结构不用去做改变,只需要设置加密字段就可以了;

2、即便是系统管理员,在没有密钥连接的情况下,看到的数据也是密文;

这个样的好处是,有权限的系统维护可以进行明文查看数据,而且在开发阶段完全可以不考虑字段的加密,在生产阶段一并处理就可以了。

这对数据库的开发无疑是非常友好的,为加密需要付出的工作量基本可以忽略不计了。

前提是你选择的数据库要支持该功能。

四、另外两个问题
1、TDE 透明数据加密
数据的加密还有一种方式,就是TDE 透明数据加密,对整个数据库文件加密。不过这只是数据库的安全手段,与我们讨论的敏感个人信息加密没有直接关系。
按照合规要求,敏感个人信息必须做到「应用层 / 字段级加密,运维 / DBA 无法直接查看明文」,TDE 只是防硬盘被盗、防数据库文件被拷贝拖库防不了内部人员、有库权限的人查明文。
2、分开存储是分库存储还是分表存储

敏感字段的分开存储要求是:

敏感个人信息与一般业务数据逻辑隔离 / 存储隔离

并没有强制要求必须分库。

以下是两种存储方式的比较、差别以及适用情形:

卓建数据合规团队

卓建律师事务所数据合规团队是国内最早从事数据合规业务的律师事务所之一,卓建数据合规研究院是律所为数据合规成立的专门机构,数据合规团队有近20名DPO律师,在个人信息保护、数据出境和数据资产交易方面积累了大量的客户和案例。

李兰兰律师
13418782876
李兰兰律师,广东卓建律师事务所高级合伙人,卓建数据合规中心主任,广东省律师协会合规与风控法律专业委员会主任,深圳市律师协会数据合规法律专业委员会主任,拥有20多年法律服务经验,现专注于企业合规和数字治理设计,是国内最早从事数据合规业务的律师之一。
推荐阅读:
如何落实敏感信息“加密”和“分开存储”的合规要求?
GDPR合规自查评估(附程序)
我们企业的数据到底是不是“重要数据”?
欧盟EDPB发布新的DPIA模板
《数据法案》的数据“自行获取”的合规要求
《数据法案》中必须共享的数据类型示例

欧盟《数据法案》(DA)核心内容及企业合规要求

《数据法案》全文条款十分钟快速预览
一图看懂欧盟《数据法案》
《数据法案》小微企业的豁免情形
产品出口欧盟的合规新要求:《数据法案》

GDPR 软件产品开发合规指南

十个GDPR处罚案例:访问权、处理目的、敏感数据、存储期限和系统安全

欧盟《人工智能法案》(AI Act)核心内容及企业合规要求

车企出海欧盟的数据合规要求

智能设备出口欧盟的合规要求

SaaS软件出海数据合规(2):数据处理者的合规要求

数据合规中的“同意”实务操作
出海企业的人工智能合规
出海企业如何实现GDPR的快速合规?
欧盟《数字服务法》发威,TikTok被调查
《数据法案》数据共享的形式、方式和格式
如何为数据合规投保?投保流程和赔偿范围