加密后的敏感字段如何实现模糊查询?

前言

开始的时候遇到了一个问题?敏感字段数据是加密存储在数据库的表中,如果需要对这些敏感字段进行模模糊查询,还用原来的通过sql的where从句的like来模糊查询的方式肯定是不行的,那么应该怎么实现呢?

场景分析

假如有类似这样的一个场景:有一个人员管理的功能,人员信息列表的主要字段有名称、性别、手机号码、等,可以对任意一条数据进行增、删、改、查,其中姓名、手机号码字段要支持模糊查询。

简单分析一个场景,可以知道:手机号码是敏感数据,这些字段的数据是要加密存储在数据库里,在页面上展示的时候需要进行脱敏处理的。

如果用户想要查询真实姓名是包含有“李四”的所有人员信息,可以在页面上输入一个关键字,如“李四”,点击开始查询后,这个参数会传递到后台,后台会执行一条sql,如“select * from sys_person where real_name like ‘%李四%’”,执行结果中包含了所有用户真实姓名包含有“李四”的所有数据记录,如“李四”,“李四娘”等。

如果用户要查询手机号码尾号是“7507”的用户,后台执行类似与姓名模糊查询的sql,"select * from sys_person where phone like '%7507'",肯定是得不到正确的结果的,因为手机号码字段在数据库中的数据是加密后的结果,而‘7507’是明文,即模糊查询关键字与实际存储的数据不一致。

实现方案

分词密文映射表

这种方法是主流的方法。新建一张分词密文映射表,在敏感字段数据新增、修改的后,对敏感字段进行分词组合,如“15503777507”的分词组合有“155”、“0377”、“7507”等,再对每个分词进行加密,建立起敏感字段的分词密文与目标数据行主键的关联关系;在处理模糊查询的时候,对模糊查询关键字进行加密,用加密后的模糊查询关键字,对分词密文映射表进行like查询,得到目标数据行的主键,再以目标数据行的主键为条件返回目标表进行精确查询。

淘宝、阿里、拼多、京东等大厂对用户敏感数据加密后支持模糊查询都是这样的原理,下面是几个大厂的敏感字段模糊查询方案说明,有兴趣可以了解一下:

淘宝密文字段检索方案

阿里巴巴文字段检索方案

拼多多密文字段检索方案

京东密文字段检索方案

这种方法的优点就是原理简单,实现起来也不复杂,但是有一定的局限性,算是一个对性能、业务相折中的一个方案,相比较之下,在能想的方法中,比较推荐这种方法,但是要特别注意的是,对模糊查询的关键字的长度,要在业务层面进行限制;以手机号为例,可以要求对模糊查询的关键字是四位或者是五位,具体可以再根据具体的场景进行详细划分。

为什么要增加这样的限制呢?因为明文加密后长度为变长,有额外的存储成本和查询性能成本,分词组合越多,需要的存储空间以及所消耗的查询性能成本也就更大,并且分词越短,被硬破解的可能性也就越大,也会在一定程度上导致安全性降低;

环境配置

  • jdk版本:1.8开发工具:Intellij iDEA 2020.1

  • springboot:2.3.9.RELEASE

  • mybatis-spring-boot-starter:2.1.4

依赖配置

示例主要用到了SpringAop,加密是对称加密,用到了hutool工具包里的加密解密工具类,也可以使用自己封装的加密解密工具类。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.3.3</version>
</dependency>

代码实现

1、新建分词密文映射表;

如果是多个模糊查询的字段,可以共用在一张分词密文映射表中扩展多个字段,以示例中的人员管理功能为例,新建sys_person_phone_encrypt表(人员的手机号码分词密文映射表),用于存储人员id与分词组合密文的映射关系

(
   id bigint auto_increment comment '主键' primary key,
   person_id int not null comment '关联人员信息表主键',
   phone_key varchar(500) not null comment '手机号码分词密文'
)
comment '人员的手机号码分词密文映射表';

2、敏感字段数据在保存入库的时候,对敏感字段进行分词组合并加密码,存储在分词密文映射表;

在注册人员信息的时候,先取出通过AOP进行加密过的手机号码进行解密;手机号码解密之后,对手机号码按照连续四位进行分词组合,并对每一个手机号码的分词进行加密,最后把所有的加密后手机号码分词拼接成一个字符串,与人员id一起保存到人员的手机号码分词密文映射表;

    this.personDao.insert(person);
    String phone = this.decrypt(person.getPhoneNumber());
    String phoneKeywords = this.phoneKeywords(phone);
    this.personDao.insertPhoneKeyworkds(person.getId(),phoneKeywords);
    return person;
}
private String phoneKeywords(String phone) {
    String keywords = this.keywords(phone, 4);
    System.out.println(keywords.length());
    return keywords;
}

//分词组合加密
private String keywords(String word, int len) {
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < word.length(); i++) {
        int start = i;
        int end = i + len;
        String sub1 = word.substring(start, end);
        sb.append(this.encrypt(sub1));
        if (end == word.length()) {
            break;
        }
    }
    return sb.toString();
}
public String encrypt(String val) {
    //这里特别注意一下,对称加密是根据密钥进行加密和解密的,加密和解密的密钥是相同的,一旦泄漏,就无秘密可言,
    //“fanfu-csdn”就是我自定义的密钥,这里仅作演示使用,实际业务中,这个密钥要以安全的方式存储;
    byte[] key = SecureUtil.generateKey(SymmetricAlgorithm.DES.getValue(), "fanfu-csdn".getBytes()).getEncoded();
    SymmetricCrypto aes = new SymmetricCrypto(SymmetricAlgorithm.DES, key);
    String encryptValue = aes.encryptBase64(val);
    return encryptValue;
}
public String decrypt(String val) {
    //这里特别注意一下,对称加密是根据密钥进行加密和解密的,加密和解密的密钥是相同的,一旦泄漏,就无秘密可言,
    //“fanfu-csdn”就是我自定义的密钥,这里仅作演示使用,实际业务中,这个密钥要以安全的方式存储;
    byte[] key = SecureUtil.generateKey(SymmetricAlgorithm.DES.getValue(), "fanfu-csdn".getBytes()).getEncoded();
    SymmetricCrypto aes = new SymmetricCrypto(SymmetricAlgorithm.DES, key);
    String encryptValue = aes.decryptStr(val);
    return encryptValue;
}

3、模糊查询的时候,对模糊查询关键字进行加密,以加密后的关键字密文为查询条件,查询密文映射表,得到目标数据行的id,再以目标数据行id为查询条件,查询目标数据表;

根据手机号码的四位进行模糊查询的时候,以加密后模糊查询的关键字为条件,查询sys_person_phone_encrypt表(人员的手机号码分词密文映射表),得到人员信息id;再以人员信息id,查询人员信息表;

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

推荐阅读更多精彩内容