加密的手机号,如何模糊查询?

我们都知道,在做系统设计时,考虑到系统的安全性,需要对用户的一些个人隐私信息,比如:登录密码、身份证号、银行卡号、手机号等,做加密处理,防止用户的个人信息被泄露。

很早之前,CSDN 遭遇了 SQL 注入,导致了 600 多万条明文保存的用户信息被泄。

因此,我们在做系统设计的时候,要考虑把用户的隐私信息加密保存。

常见的对称加密算法有 AES、SM4、ChaCha20、3DES、DES、Blowfish、IDEA、RC5、RC6、Camellia 等。

目前国际主流的对称加密算法是AES,国内主推的则是SM4

无论是用哪种算法,加密前的字符串和加密后的字符串,差别还是比较大的。

比如加密前的字符串:苏三说技术,使用密钥:123,生成加密后的字符串为:o3kxBSmFGeRSzj2uwGoGA==

如何对加密后的字符串做模糊查询呢?

比如:假设查询苏三关键字,加密后的字符串是:U2FsdGVkX19eCv+xt2WkQb5auYo0ckyw

上面生成的两个加密字符串差异看起来比较大,根本没办法直接通过 SQL 语句中的 like 关键字模糊查询。

那我们该怎么实现加密的手机号的模糊查询功能呢?

1 一次加载到内存

实现这个功能,我们第一个想到的办法可能是:把个人隐私数据一次性加载到内存中缓存起来,然后在内存中先解密,再在代码中实现模糊搜索的功能。

5aee78b6752ace4ca11494a842714b67.png

这样做的好处是:实现起来比较简单,成本非常低。

但带来的问题是:如果个人隐私数据非常多的话,应用服务器的内存不一定够用,可能会出现 OOM 问题。

还有另外一个问题是:数据一致性问题。

如果用户修改了手机号,数据库更新成功了,需要同步更新内存中的缓存,否则用户查询的结果可能会跟实际情况不一致。

比如:数据库更新成功了,内存中的缓存更新失败了。

或者你的应用,部署了多个服务器节点,有一部分内存缓存更新成功了,另外一部分刚好在重启,导致更新失败了。

该方案可能会导致应用服务器出现 OOM 问题,也可能会导致系统的复杂度提升许多,总体来说,有点得不偿失。

2 使用数据库函数

既然数据库中保存的是加密后的字符串,还有一种方案是使用数据库的函数解密。

我们可以使用 MySQL 的 DES_ENCRYPT 函数加密,使用 DES_DECRYPT 函数解密:

SELECT DES_DECRYPT('o3kxBSmFGeRSzj2uwGoGA==', '123'); 

应用系统中所有的用户隐私信息的加解密都在 MySQL 层实现,不存在加解密不一致的情况。

该方案中保存数据时,只对单个用户的数据进行操作,数据量比较小,性能还好。

但模糊查询数据时,每一次都需要通过 DES_DECRYPT 函数,把数据库中用户某个隐私信息字段的所有数据都解密了,然后再通过解密后的数据做模糊查询。

如果该字段的数据量非常大,这样每次查询的性能会非常差。

3 分段保存

我们可以将一个完整的字符串,拆分成多个小的字符串。

以手机号为例:18200256007,按每 3 位为一组进行拆分,拆分后的字符串为:182,820,200,002,025,256,560,600,007,这 9 组数据。

然后建一张表:

CREATE 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='分段加密映射表'

这张表有三个字段:

  • id:系统编号。
  • ref_id:主业务表的系统编号,比如用户表的系统编号。
  • encrypt_value:拆分后的加密字符串。

用户在写入手机号的时候,同步把拆分之后的手机号分组数据也一起写入,可以保证在同一个事务当中数据的一致性。

    public static function encrypt(){
        
        $mobile = '苏三说技术';
        $ciphertext = openssl_encrypt($mobile, 'aes-128-ecb', '123');
        print_r($ciphertext) ;
    }   
     /**
     * 解密
     */
    public static function mobile_decrypt(){
        $encrypted_str = 'zo3kxBSmFGeRSzj2uwGoGA==';
        print_r(openssl_decrypt($encrypted_str, 'aes-128-ecb', '123')) ;
    }

如果要模糊查询手机号,可以直接通过 encrypt_value_mapping 的 encrypt_value,模糊查询出用户表的 ref_id,再通过 ref_id 查询用户信息。

具体 sql 如下:

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 = 'U2FsdGVkX19Se8cEpSLVGTkLw/yiNhcB'
limit 0,20;

这样就能轻松地通过模糊查询,搜索出我们想要的手机号了。

注意这里的 encrypt_value 用的等于号,由于是等值查询,效率比较高。

注意:这里通过 sql 语句查询出来的手机号是加密的,在接口返回给前端之前,需要在代码中统一做解密处理。

为了安全性,还可以将加密后的明文密码,用*号增加一些干扰项,防止手机号被泄露,最后展示给用户的内容,可以显示成这样的:182***07

4 其他的模糊查询

如果除了用户手机号,还有其他的用户隐私字段需要模糊查询的场景,该怎么办?

我们可以将 encrypt_value_mapping 表扩展一下,增加一个type字段。

该字段表示数据的类型,比如:1.手机号 2.身份证 3.银行卡号等。

这样如果有身份证和银行卡号模块查询的业务场景,我们可以通过 type 字段做区分,也可以使用这套方案,将数据写入到 encrypt_value_mapping 表,最后根据不同的 type 查询出不同的分组数据。

如果业务表中的数据量少,这套方案是可以满足需求的。

但如果业务表中的数据量很大,一个手机号就需要保存 9 条数据,一个身份证或者银行卡号也需要保存很多条数据,这样会导致 encrypt_value_mapping 表的数据急剧增加,可能会导致这张表非常大。

最后的后果是非常影响查询性能。

那么,这种情况该怎么办呢?

5 增加模糊查询字段

如果数据量多的情况下,将所有用户隐私信息字段,分组之后,都集中到一张表中,确实非常影响查询的性能。

那么,该如何优化呢?

答:我们可以增加模糊查询字段。

还是以手机模糊查询为例。

我们可以在用户表中,在手机号旁边,增加一个 encrypt_phone 字段。

CREATE TABLE `user` (
  `id` int NOT NULL,
  `code` varchar(20)  NOT NULL,
  `age` int NOT NULL DEFAULT '0',
  `name` varchar(30) NOT NULL,
  `height` int NOT NULL DEFAULT '0',
  `address` varchar(30)  DEFAULT NULL,
  `phone` varchar(11) DEFAULT NULL,
  `encrypt_phone` varchar(255)  DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='用户表'

然后我们在保存数据的时候,将分组之后的数据拼接起来。

还是以手机号为例:

18200256007,按每 3 位为一组,进行拆分,拆分后的字符串为:182,820,200,002,025,256,560,600,007,这 9 组数据。

分组之后,加密之后,用逗号分割之后拼接成这样的数据:,U2FsdGVkX19Se8cEpSLVGTkLw/yiNhcB,U2FsdGVkX1+qysCDyVMm/aYXMRpCEmBD,U2FsdGVkX19oXuv8m4ZAjz+AGhfXlsQk,U2FsdGVkX19VFs60R26BLFzv5nDZX40U,U2FsdGVkX19XPO0by9pVw4GKnGI3Z5Zs,U2FsdGVkX1/FIIaYpHlIlrngIYEnuwlM,U2FsdGVkX19s6WTtqngdAM9sgo5xKvld,U2FsdGVkX19PmLyjtuOpsMYKe2pmf+XW,U2FsdGVkX1+cJ/qussMgdPQq3WGdp16Q。

以后可以直接通过 sql 模糊查询字段 encrypt_phone 了:

select id,name,phonefrom user where encrypt_phone like '%U2FsdGVkX19Se8cEpSLVGTkLw/yiNhcB%'limit 0,20;

注意这里的 encrypt_value 用的like

这里为什么要用逗号分割呢?

答:是为了防止直接字符串拼接,在极端情况下,两个分组的数据,原本都不满足模糊搜索条件,但拼接在一起,却有一部分满足条件的情况发生。

当然我们也可以根据实际情况,将逗号改成其他的特殊字符。

此外,其他的用户隐私字段,如果要实现模糊查询功能,也可以使用类似的方案。

最后说一句,虽说本文介绍了多种加密手机号实现模糊查询功能的方案,但我们要根据实际业务场景来选择,没有最好的方案,只有最合适的。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,347评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,435评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,509评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,611评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,837评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,987评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,730评论 0 267
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,194评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,525评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,664评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,334评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,944评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,764评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,997评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,389评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,554评论 2 349

推荐阅读更多精彩内容