问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

敏感加密信息模糊查询解决方案

创作时间:
作者:
@小白创作中心

敏感加密信息模糊查询解决方案

引用
CSDN
1.
https://blog.csdn.net/qq_45914687/article/details/133067497

在数据库中存储加密敏感信息时,如何实现模糊查询是一个常见的技术问题。本文将介绍几种解决方案,并重点推荐"分词密文映射表"方法,该方法通过建立分词密文映射表来实现模糊查询,同时提供了具体的代码实现和环境配置说明。

前言

在实际应用中,敏感字段数据(如手机号码、身份证号码等)通常需要加密存储在数据库中。然而,这种加密存储方式给模糊查询带来了挑战。传统的SQL LIKE查询方式在这种场景下不再适用,因为查询关键字是明文,而数据库中存储的是加密后的数据。本文将探讨几种解决方案,并重点推荐一种在实际生产环境中较为实用的方法。

场景分析

假设有一个人员管理功能,主要字段包括姓名、性别、用户账号、手机号码、身份证号码、家庭住址和注册日期等。其中,姓名、身份证号码和手机号码需要支持模糊查询。由于手机号码、身份证号码和家庭住址等字段包含敏感信息,这些字段的数据需要加密存储在数据库中,并在页面展示时进行脱敏处理。

例如,用户想要查询所有真实姓名包含"张三"的人员信息,可以在页面上输入关键字"张三"。后台会执行类似以下的SQL查询:

SELECT * FROM sys_person WHERE real_name LIKE '%张三%'

但是,如果用户要查询手机号码尾号为"0537"的用户,直接使用类似SQL查询:

SELECT * FROM sys_person WHERE phone LIKE '%0537'

将无法得到正确结果,因为手机号码字段在数据库中存储的是加密后的数据,而查询关键字是明文。

实现方案

下面介绍几种可能的解决方案:

第一种:先解密再查询

这种方法是在内存中解密所有目标表的数据,然后遍历解密后的数据,与模糊查询关键字进行比较,筛选出包含关键字的数据行。虽然这种方法容易想到,但存在明显的缺点:如果数据量很大,很容易导致内存溢出,因此不推荐在生产环境中使用。

第二种:明文映射表

创建一张映射表,存储敏感字段解密后的数据与目标表主键的映射关系。查询时先对明文映射表进行模糊查询,获取符合条件的目标数据主键,再根据主键查询目标表。这种方法实际上相当于没有对敏感字段进行加密存储,违背了加密存储的初衷,因此也不推荐在生产环境中使用。

第三种:数据库层面解密查询

在执行查询SQL时对敏感字段先解密,然后再执行LIKE查询。例如:

SELECT * FROM sys_person WHERE AES_DECRYPT(phone, 'key') LIKE '%0537'

这种方法的优点是实现成本低,但存在明显缺点:无法利用数据库索引优化查询,且某些数据库可能无法保证加解密算法的一致性,导致加密和解密出现不匹配的问题。因此,这种方法也不推荐在生产环境中使用。

第四种:分词密文映射表

这种方法是对第二种方法的优化。在敏感字段数据新增或修改后,对敏感字段进行分词组合,例如"15503770537"可以分为"155"、"0377"、"0537"等,然后对每个分词进行加密,建立敏感字段分词密文与目标数据行主键的关联关系。查询时,对模糊查询关键字进行加密,用加密后的关键字对分词密文映射表进行LIKE查询,获取目标数据行的主键,再根据主键精确查询目标表。

例如,手机号"13518543037"按4个字符一组分词后会得到以下8组分词:

  • 1351
  • 3518
  • 5185
  • 1854
  • 8543
  • 5430
  • 4303
  • 3037

将这8组分词分别加密后,组合起来作为敏感字段的检索关键字。

这种方法的优点是原理简单,实现起来也不复杂,但有一定的局限性。建议在业务层面限制模糊查询关键字的长度,例如手机号可以要求4位或5位,以平衡存储成本、查询性能和安全性。

环境配置

  • JDK版本:1.8
  • 开发工具:IntelliJ IDEA 2020.1
  • Spring Boot版本:2.3.9.RELEASE
  • MyBatis版本:2.1.4

依赖配置

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

<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.8.21</version>
</dependency>

代码实现

  1. 新建分词密文映射表

如果是多个模糊查询的字段,可以共用一张分词密文映射表。以人员管理功能为例,新建sys_person_phone_encrypt表,用于存储人员ID与分词组合密文的映射关系。

-- 信息基础表
CREATE TABLE `sys_person` (
  `id` bigint NOT NULL COMMENT '编号',
  `name` varchar(100) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '姓名',
  `age` int DEFAULT NULL COMMENT '年龄',
  `phone` varchar(125) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '手机号',
  `phone_cipher` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '手机号密文',
  `id_card` varchar(225) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '身份证号',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='用户基本信息';

-- 敏感字段映射表
CREATE TABLE `sys_person_phone_encrypt` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
  `person_id` bigint NOT NULL COMMENT '关联人员信息表主键',
  `phone_key` varchar(500) COLLATE utf8mb4_general_ci NOT NULL COMMENT '手机号码分词密文',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1704319384342601730 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='人员的手机号码分词密文映射表';
  1. 工具类
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.symmetric.SymmetricAlgorithm;
import cn.hutool.crypto.symmetric.SymmetricCrypto;

public class SensitiveWordUtil {
    private final static String privateKey = "RiceColor";

    /**
     * 加密后的组合
     */
    public static String phoneKeywords(String phone) {
        String keywords = keywords(phone, 4);
        System.out.println(keywords.length());
        return keywords;
    }

    /**
     * 分词组合加密
     */
    public static 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(encrypt(sub1));
            if (end == word.length()) {
                break;
            }
        }
        return sb.toString();
    }

    /**
     * 加密
     */
    public static String encrypt(String val) {
        byte[] key = SecureUtil.generateKey(SymmetricAlgorithm.DES.getValue(), privateKey.getBytes()).getEncoded();
        SymmetricCrypto aes = new SymmetricCrypto(SymmetricAlgorithm.DES, key);
        String encryptValue = aes.encryptBase64(val);
        return encryptValue;
    }

    /**
     * 解密
     */
    public static String decrypt(String val) {
        byte[] key = SecureUtil.generateKey(SymmetricAlgorithm.DES.getValue(), privateKey.getBytes()).getEncoded();
        SymmetricCrypto aes = new SymmetricCrypto(SymmetricAlgorithm.DES, key);
        String encryptValue = aes.decryptStr(val);
        return encryptValue;
    }
}
  1. 模糊查询实现

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

例如,根据手机号码的四位进行模糊查询时,以加密后的模糊查询关键字为条件,查询sys_person_phone_encrypt表,得到人员信息ID;再以人员信息ID,查询人员信息表。

查询结果

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号