数据库里的手机号、身份证号、银行卡号要加密,这件事已经没有太大争议。“怎么把数据加密?”
“加密以后,业务还怎么查?”
SELECT *FROM userWHERE mobile = '13800138000';
SELECT *FROM person aJOIN visit b ON a.id_card = b.id_card;
SELECT *FROM userWHERE mobile LIKE '138%';
如果手机号、身份证已经通过 SM4、AES 等算法变成密文:13800138000 ↓8F3A91C72E5B......
让数据“看不懂”并不难,难的是看不懂之后,数据库还得继续干活。
一、为什么一加密,WHERE、JOIN、LIKE 就麻烦了?
普通数据库之所以能够查询,是因为数据库能够看到数据之间的关系。13800138000 = 13800138000
WHERE =JOIN =GROUP BYDISTINCT
数据库能够看到字符串内容,所以知道这个手机号以 138 开头。才能支持 >、<、BETWEEN、ORDER BY 等查询。但采用高安全性的随机化字段加密后,数据库看到的只是一串不可读密文,原有的相等关系、字符结构和大小关系也可能一起消失。既希望数据库“什么都看不到”,又希望数据库“什么都能查”。
想保留什么查询能力,就必须为这种查询能力设计对应的处理机制。
二、WHERE、JOIN、LIKE 到底怎么解决?
1. WHERE:等值查询相对最好解决
WHERE mobile = '13800138000'
判断两个值是不是相等。
一种办法是确定性保护,让相同明文产生相同的可比较结果:13800138000 → A83F21...13800138000 → A83F21...
数据库虽然不知道 A83F21... 到底是什么手机号,但它知道两个值相等。另一种更灵活的工程思路,是把存储保护和查询能力拆开:13800138000 │ ├── 强加密密文 → 8F39A2...... │ └── 查询标识 → B78219......
原始数据继续采用强加密保护,而查询时使用专门的安全查询标识。不要强迫一个密文同时承担“数据安全”和“所有查询能力”。
2. JOIN:关键是建立一致、受控的安全关联表示
A 和 B 是不是同一个值。
因此可以把原始身份证号保护起来,同时为允许关联的场景生成一致的安全关联表示:表 A
身份证密文:Enc(ID)关联标识:Token(ID) 表 B身份证密文:Enc(ID)关联标识:Token(ID)
A.Token(ID) = B.Token(ID)
是不是所有系统都应该能够认出“这是同一个人”?
医院、医保、体检、保险,如果全部使用完全相同的关联标识,即便看不到身份证明文,也可能形成跨系统关联。所以 JOIN 真正需要解决的不只是“能不能关联”,还要解决:谁可以和谁关联。
也就是说,关联标识还要考虑业务域、租户、系统边界和授权范围。3. LIKE:通常是最难的一类
这已经不是简单判断 A = B,而是要求数据库理解字段内部的字符结构。一种现实的处理方法,是针对业务真正需要的检索能力生成受保护的检索信息。或者针对文本建立分词、N-gram、前缀 Token 等查询辅助结构。不再直接扫描原始密文,而是查询对应的安全检索信息。如果是 %关键词%、范围比较、复杂表达式等更复杂场景,则可以进一步结合 TEE 等可信执行能力处理。“换一种加密算法就解决了。”
加密负责把数据藏起来,查询机制负责把数据找出来。
三、解决这些问题,目前主要有 6 条技术路线
把前面的问题放在一起看,目前常见的处理思路大致可以归纳为六类。1. 随机化加密
2. 确定性加密
WHERE =部分等值 JOINGROUP BYDISTINCT
3. 格式保持加密 FPE
例如手机号原来必须是 11 位数字,加密以后仍保持 11 位数字,减少字段长度、数据格式和外围接口改造。格式保持,不等于查询语义保持。
手机号加密后仍然是 11 位数字,并不代表原来的 LIKE '138%' 还能直接使用。4. 可搜索加密 / 检索辅助
加密负责保护数据,检索结构负责查询。
根据业务需要,可以针对等值、前缀、关键词甚至特定模糊查询设计相应的检索能力。它并不是让普通密文本身突然“什么都能查”,而是给需要保留的查询能力配套专门机制。5. TEE
如果复杂查询必须接触明文,那就把明文限制在可信执行环境里。
必要的解密、比较、JOIN、LIKE、范围处理等操作可以放到受保护的执行边界中完成。它解决的重点不是“密文如何直接完成所有 SQL”,而是:明文允许在哪里出现。
6. 同态加密
不恢复明文,也可以直接对密文完成特定计算。
它在求和、统计、加权计算、特定隐私计算等场景中很有价值。密码学上能计算,不等于数据库能够像以前一样高效执行 SQL。
普通数据库背后还有 B-tree、Hash Join、统计信息、执行计划、排序、缓存、并行执行等完整体系。“把 SM4 换成同态加密,WHERE、JOIN、LIKE 就全部解决了。”
四、真正落地时,往往不是“六选一”
更常见的情况是:根据不同字段、不同查询方式组合使用。 | |
|---|
| |
WHERE = | |
| |
LIKE '138%' | |
LIKE '%关键词%' | |
> | |
| |
所以,数据库字段加密真正的工程答案并不是寻找一种“万能算法”,而是:先看业务到底怎么用数据,再决定需要保留哪些查询能力。
五、真正难的,不是 encrypt(),而是尽量不影响原有业务
如果只是一个新开发的小系统,开发人员当然可以自己改代码、改 SQL、增加辅助字段。几十套业务系统数百张业务表成千上万条 SQL多个数据库品牌大量历史系统第三方成品软件甚至部分系统无法修改源码
什么时候加密?什么时候解密?什么时候生成查询标识?WHERE 怎么查?JOIN 怎么关联?LIKE 怎么处理?谁能看到明文?谁只能看到脱敏结果?
因此,真正落地时越来越重要的一件事,是把安全能力尽量从业务代码中抽离出来。业务仍然按照熟悉的方式访问数据库,而数据保护机制在业务与数据库之间统一处理:业务访问 ↓识别 SQL 和敏感字段 ↓根据策略选择处理方式 ↓加密 / 查询辅助 / 解密 / 脱敏 / 过滤 ↓数据库
把复杂性留在安全侧,让业务侧尽量简单。
数据库加密最终要解决的,不只是“字段有没有变成密文”,而是:敏感数据保护以后,原有业务还能不能尽量照常运行。
六、真正的答案,其实很简单
数据库中手机号、身份证加密后,WHERE、JOIN、LIKE 还能用吗?
加密负责把数据藏起来,查询机制负责把数据找出来。
复杂查询,再根据实际需求考虑 TEE 等更合适的技术。“到底哪一种加密技术最先进?”
“这个字段加密以后,业务还需要怎么用?”
把数据加密完就结束。
既要让不该看的人看不懂,又要让该用的人照常用。
阅读原文:原文链接
该文章在 2026/10/21 12:25:56 编辑过