我们都知道在做系统设置的时候要考虑系统的安全性,需要对一些用户的个人隐私信息,比如:登录密码,身份证号,银行卡号,手机号等进行加密处理,防止用户的个人隐私被泄露。
常见的加密算法有AES、SM4、ChaCha20、3DES、DES、Blowfish、IDEA、RC5、RC6、Camellia等
目前国际主流的对称加密算法是AES,国内主推的则是SM4
无论是用哪种算法,加密前的字符串和加密后的字符串,差别还是比较大的
比如加密前的字符串:123456
使用密钥:WPUOa5zl8Niyl89RcF6qbg==
生成加密后的字符串为:xlK5on8X4FVBCXhrW1LGRw==
如何对加密后的字符串做模糊查询呢
比如查询123关键字
生成加密后的字符串为:NFyMmH+NFmjQwe7W8i8iyQ==
上面生成的两个字符串差异看起来比较大,根本没办法直接通过SQL语句中的like关键字模糊查询
那我们该怎么实现加密的手机号的模糊查询呢?
1.一次加载到内存
实现这个功能,我们第一个想到的办法可能是把个人隐私数据一次性加载到内存中缓存起来,然后在内存中先解密然后在代码中实现模糊搜索的功能
图片这样做的好处是:实现起来比较简单,成本非常低
但带来的问题是:如果个人隐私数据非常多的话,应用服务器的内存不一定够用,可能会出现OOM问题
还有另外一个问题是:数据一致性问题
如果用户修改了手机号,数据库更新成功了,需要同步更新内存中的缓存,否则用户查询的结果可能会跟实际情况不一致
比如:数据库更新成功了,内存中的缓存更新失败了,或者你的应用部署了多个服务器节点,有一部分内存缓存更新成功了,另一部分刚好在重启,导致更新失败了
该方案不仅可能会导致应用服务器出现OOM问题,也可能会导致系统的复杂度提升许多
总体来说有点得不偿失
2.使用数据库函数
既然数据库中保存的是加密后的字符串,还有一种方案是使用数据库的函数解密
我们可以使用MySQL的DES_ENCRYPT函数加密,使用DES_DECRYPT函数解密
如图所示:应用系统中所有的用户隐私信息的加解密都在MySQL层实现,不存在加解密不一致的情况
该方案中保存数据时只对单个用户的数据进行操作,数据量比较小,性能还好,但模糊查询数据时,每一次都需要通过DES_DECRYPT函数把数据库用用户某个隐私信息字段的所有数据解密了,然后再通过解密后的数据做模糊查询,如果该字段的数据量非常大,这样每次查询的性能会非常差
3.分段保存
我们可以将一个完整的字符串拆分成多个小的字符串,以手机号为例16602740823按每3位为一组进行拆分,0-3之后1-4
拆分后的字符串为:166660602027274740408082823,这九组数据
然后建一张表:这张表有三个字段
id:系统编号
ref_id:主业务表的系统编号,比如用户表的系统编号
encrypt_value:拆分后的加密字符串
用户在写入手机号的时候
同步把拆分之后的手机号分组数据也一起写入,可以保证在同一个事务当中,保证数据的一致性
如果要模糊查询手机号,可以直接通过encrypt_value_mapping的encrypt_value模糊查询出用户表的ref_id再通过ref_id查询用户信息
具体的sql如下:
SELECT
s2.id,
s2.username,
s2.phone
FROM
encrypt_value_mapping s1
INNER JOIN `user` s2 ON s1.ref_id = s2.id
WHERE
s1.encrypt_value = 'dR4ZDy1dygKQ5eppuprE/g=='
这样就能通过模糊查询搜索出我们想要的手机号了,这里的encrypt_value用的是=,由于是等值查询,效率比较高
注意:这里通过sql语句查询出来的手机号是加密的,在接口返回给前端之前需要在代码中统一做解密处理,为了安全性,还可以将解密后的明文用*增加一些干扰项,防止手机号被泄露,最后展示给用户的内容可以是166*****823
其他的模糊查询,如果除了用户手机号还有其他的用户隐私字段需要模糊查询的场景,我们可以将encrypt_value_mapping表扩展一下,增加一个type字段,该字段表示数据的类型
比如:1.手机号、2.身份证、3.银行卡号等
这样如果有身份证和银行卡号模块查询的业务场景,我们可以通过type字段做区分,也可以使用这套方案,将数据写入到encrypt_value_mapping表,最后根据不同的type查询出不同的分组数据
如果业务表中的数据量少,这套方案是可以满足需求的,但如果业务表中的数据量很大,一个手机号就需要保存9条数据,一个身份证或者银行卡号也需要保存很多条数据,这样会导致encrypt_value_mapping表的数据急剧增加,可能会导致这张表非常大,最后的后果是非常影响查询性能,那么这种情况该怎么办呢?
4.增加模糊查询字段
如果数据量多的情况下,将所有用户隐私信息字段分组之后都集中到一张表中,确实非常影响查询的性能,那么该如何优化呢?
可以增加模糊查询字段,还是以手机模糊查询为例
我们可以在用户表中在手机号旁边增加一个encrypt_phone字段,然后我们在保存数据的时候,将分组之后的数据拼接起来,还是以手机号为例:16602740823按每3位为一组进行拆分,拆分后的字符串为:166660602027274740408082823,这九组数据,分组之后,加密之后,用逗号分割之后拼接成这样的数据:
,sQVhAb+oiW6hLkVumIE+6g==,TrF7yedWolkcAL8+x4GlEw==,rwa99pU5dppMptqrnY01vw==,uvU9H7eGVSaOI4GXJK6nlA==,sJVLu1Rxxu3b0ZgaO9e/gw==,fypz7O6dH5284c+ZN0P5qQ==,ugNdCW4FjRNvTBuEdW08PQ==,51gRUxwSngLsUmCfqpZ2wA==,dR4ZDy1dygKQ5eppuprE/g==
以后可以直接通过sql模糊查询字段encrypt_phone了
具体sql如下:
SELECT
id,
username,
phone
FROM
`user`
WHERE
encrypt_phone LIKE '%sQVhAb+oiW6hLkVumIE+6g==%'
注意这里的encrypt_value用的like
为什么分组加密之后拼接要用逗号分隔呢?
是为了防止直接字符串拼接在极端情况下,两个分组的数据原本都不满足模糊搜索条件,但拼接在一起却有一部分满足条件的情况发生,当然也可以根据实际情况,将逗号改成其他的特殊字符,此外,其他的用户隐私字段如果要实现模糊查询功能,也可以使用类似的方案
虽然本文介绍了多种加密手机号实现模糊查询功能的方案,但我们要根据实际业务场景来选择,没有最好的方案,只有最适合的
标签:encrypt,手机号,模糊,查询,字符串,加密 From: https://www.cnblogs.com/zhao-zong-yu-hai/p/18025611