加密数据如何实现模糊查询
解密后存储到内存「不推荐」
准备
- 将数据加载到内存中,将密文数据解密得到明文
- 创建映射Map,例如明文—数据 ID,存储到内存
- 数据新增、修改【密文数据更新】、删除时,需要同步更新内存中的数据
使用
- 后端代码,从内存中取出映射数据,根据 key 模糊查询,筛选出满足条件的 value 即数据 ID
- 根据数据 ID 过滤数据
缺点
- 数据量大时,会出现 OOM 问题
- 需要保证数据一致性,也就是数据库数据变化时需要同步更新内存中的数据
- 多实例部署时,需要解决多实例服务内存中数据一致性问题。对于分布式系统来说,这会增加系统的复杂度,得不偿失
- 一个后端服务实例数据变化时,除了更新当前服务内存中数据,还要通知其它服务实例更新内存数据
- 通知会有延迟问题,还要保证通知必达,一定执行等
解密后存储到缓存「不推荐」
与上述方案类似,但将解密后的数据放入 Redis 中,虽然解决了多服务实例时数据一致性问题,但解密后的数据放入 Redis,这本身就带来了数据泄露的隐患
使用数据库DES、AES 加密解密函数「不推荐」
- 在 MySQL 中使用
DES_ENCRYPT
或DES_DECRYPT
函数,或者更安全的AES_DECRYPT()
或AES_DECRYPT
函数 - 缺点:由于解密的开销,这种查询可能比普通的文本搜索慢得多,不推荐使用
1 | SELECT * FROM encrypted_data |
增加分段加密数据字段「大数量时不推荐」
准备
- 例如手机号字段
phone
存储的是加密后的手机号,再增加一个模糊查询字段encrypt_fragment_phone
- 将手机号按照每连续 4 位一组拆分,得到 8 组数据
- 每组数据加密,然后使用逗号将加密后的数据连接起来,存储到字段
encrypt_fragment_phone
中
使用
- 前端输入 4 位手机号
1 | # f4G71mp+tJbCIQGOmnISkA== 为前端输入的 4 位手机号加密后的数据 |
缺点
- 模糊匹配不管是
like
还是find_in_set
效率并不高,特别是数据量大时,查询速度比较慢,不推荐
增加扩展表,保存分段加密数据「推荐」
准备
增加扩展表
encrypt_value_mapping
,并为ref_id
、encrypt_value
创建索引,提高联表查询效率1
2
3
4
5CREATE TABLE `encrypt_value_mapping` (
`id` bigint NOT NULL COMMENT '系统编号',
`ref_id` bigint NOT NULL COMMENT '关联系统编号',
`encrypt_value` varchar(255) NOT NULL COMMENT '加密后的字符串'
) ENGINE=InnoDB CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='分段加密映射表'示例手机号,将手机号按照每连续 4 位一组拆分,得到 8 组数据
每组数据加密,对应扩展表中增加一条关联数据
使用
前端输入 4 位手机号
sql 示例
1
2
3
4
5# f4G71mp+tJbCIQGOmnISkA== 为前端输入的 4 位手机号加密后的数据
select s2.id,s2.name,s2.phone from encrypt_value_mapping s1
inner join user s2 on s1.ref_id=s2.id
where s1.encrypt_value = 'f4G71mp+tJbCIQGOmnISkA=='
limit 0,20;后端获取到加密后的手机号后,解密得到明文。此时还需要做下干扰处理,例如中间替换为
*
号,示例186******98
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 王文哲的博客!