数据库中加密的数据如何查询
先思考
- 数据库中加密的数据如何查询?
- 如何做等值查询、模糊查询、范围查询?
- 确定性加密、HMAC 哈希索引、分段/分桶存储、保序加密、N-gram 索引、布隆过滤器等设计有何特点?

目录
1 数据加解密方式
数据库数据加密的方式有加解密 API、透明加解密和应用层加解密等,其中前两种加密方式由数据库系统都会提供。
1.1 加解密 API
加解密 API 是指数据库管理系统提供的可在 SQL 语句中调用的加解密 API,应用可以利用这些 API 对涉密数据进行加密保护。其特点是灵活性强,支持列级加密,但构建和管理复杂,且应用需要适配 API 或代理接口,可能引入额外的运维负担。适用于需要平衡安全性和性能的场景。
1.2 透明加解密
透明加密是指数据库安全管理员为数据库敏感字段选择加密方式和密钥级别,应用访问受保护数据时只需要使用口令打开或关闭密钥表即可,对数据的加密和解密皆有数据库管理系统自动完成。其特点是管理方式简单、应用程序负担轻,但灵活性差;且由于其通常以数据库文件或表为单位进行加密,无法针对特点列进行细粒度加密,会导致索引优化失效。适用于需要基础存储安全防护的场景。
1.3 应用层加解密
应用层加解密是指应用程序将数据写入数据库之前在应用程序中对涉密数据进行加密,然后将加密后的密文写入数据库,访问数据时将密文读取到内存中后再通过应用程序解密,是最常用的加解密方式。其特点是灵活性高,支持细粒度加密,可按表、列甚至按行选择加密字段,但需要修改应用代码,增加了开发和维护成本,且会影响数据查询性能(加密后的数据可能会是索引失效、无法直接模糊查询和范围查询等)。适用于数据及其敏感的场景,如金融数据。
在了解了数据加解密方式后,我们再来看如何对加密后的数据进行等值、模糊和范围查询。
2 等值查询
2.1 确定性加密
确定性加密是加密时指使用固定的密钥和IV,使得相同的明文总能生成相同的密文。
例如使用 mysql 提供的基于 AES 算法的对称加密函数 AES_ENCRYPT API 加密:
insert into sys_user (id, username)
values (1, AES_ENCRYPT('zed', 'key123'));
查询时使用相同的加密函数和 IV 即可:
select * from sys_user
where username = AES_ENCRYPT('zed', 'key123');
同时也可以为加密字段添加索引,以提升查询性能。确定性加密虽然实现简单,但安全性较低,可能会泄漏数据模式,且易遭受频率分析攻击。
同理,也可以在应用层通过编程式实现这些逻辑,因为对于加密函数、哈希函数和加密方式而言,应用层有着更多更灵活的选择。
2.2 HMAC 哈希索引
HMAC 哈希索引是指基于基于加密函数与哈希函数结合密钥,生成两份数据:一份为加密值,也就是密文,主要用于显示;另一份为 HMAC 哈希值,主要用于查询。同时,这加密值和哈希值都需要存储。
使用加解密 API 加解密示例如下:
-- 插入数据时同时生成密文和索引
insert into sys_user (id, username, username_index)
values (2, AES_ENCRYPT('zed', 'key123'), SHA1('zed'));
查询时使用 HMAC 索引即可:
select * from sys_user
where username_index = SHA1('zed');
同理,也可以在应用层通过编程式实现这些逻辑,因为对于加密函数、哈希函数和加密方式而言,应用层有着更多更灵活的选择。
3 范围查询
3.1 分段/分桶存储
分桶存储是指将数值范围划分为多个区间,为每个区间设置标识/标签/桶名,插入数据时同时插入被加密数据所在的区间标识,查询先计算上下限所在区间,然后通过区间标识检索数据。
例如,将用户年龄 age 分为 [0 ~ 18] [18 ~ 30] [30 ~ 40] [40 ~ 50] 等区间,各区间对应的标识 age_bucket 为 0 1 2 3 ,则:
-- 插入数据时同时插入密文和明文所在区间
insert into sys_user (id, age, age_bucket)
values (3, AES_ENCRYPT('27', 'key123'), 1);
查询时先计算上下限所在区间标识,如查询条件为 22 ~ 35,则其区间标识分别为 1 和 2,然后对区间标识 age_bucket 进行范围查询:
select * from sys_user
where age_bucket >= 1 and age_buckt <= 2;
接着在应用层对检索到的数据的加密字段进行解密,最后再进行精确过滤,即保留 22 <= age <= 35 的记录。
分桶存储方案适用于大部分场景,但可能会泄漏数据分布,且需要在应用层进一步过滤。
3.2 保序加密
保序加密是指依赖保序加密算法,使得加密后的密文顺序与明文顺序一致,如 a < b,则 enc(a) < enc(b)。
如:
-- 插入数据时直接对涉密字段加密
insert into sys_user (id, username, age)
values (4, 'ahri', ENC('27', 'key123'));
-- 查询时
select * from sys_user
where age > ENC('22');
注:大部分主流数据库并没有提供关于保序加密的 API,故建议在应用层实现。
保序加密简单方便,但安全性较弱,会泄漏数据顺序信息,仅适用于对安全性要求不高的场景。
4 模糊查询
4.1 应用层处理
有种流传的说法,将全量数据加载到内存,然后在应用层处理。如果,我是说如果,如果可以的话,那可以试试,但还是不建议。
4.2 N-gram 索引
N-gram 索引是指将明文数据拆分成多个由 n 个连续字符组成的子串,然后对这些子串(或称为分词)进行哈希/加密存储。查询时,对查询条件进行同样的 N-gram 算法处理,并对处理后的多个分词进行哈希/加密,然后再进行检索。
例如,对 username 值为 hello 的数据进行 2-gram 算法处理得到的分词为 he el ll lo,然后对这些分词进行哈希/加密处理并存储到 bigrams 字段:
-- bigrams 值为 [hash('he'), hash('el'), hash('ll'), hash('lo')]
insert into sys_user (id, username, bigrams)
values (5, ENC('hello'), '...');
假如查询条件为 ell,则先对其进行 2-gram 处理得到分词组 el ll,然后在进行哈希/加密处理,最后在对 bigrams 字段进行检索:
-- 假设查询条件处理后的结果为 condition = [hash('el'), hash('ll')]
select * from sys_user
where bigrams like '%condition%';
最后将得到的结果在内存中进行精确模糊过滤(即先解密后匹配)。
N-gram 索引其优点是支持子串搜索,缺点是存储开销大,且安全性较低。对于其存储开销大的缺点,可通过限制子串长度(即 N)和数量来限制存储开销。N-gram 是目前较流行的一个方案。
4.3 布隆过滤器
布隆过滤器是指使用布隆过滤器存储关键词,可快速判断关键词是否存在。存储流程为:
- 1、首先从待加密文本中提取出关键词;
- 2、其次通过关键词数组生成布隆过滤器对象(如果为 java 语言,则可使用
com.google.guava依赖中的BloomFilter类); - 3、然后将布隆过滤器对象序列化得到其字节数组对象;
- 4、最后将密文和字节数组对象写入数据库;
查询流程为:
- 1、从数据库中读取出所有布隆过滤器对应的字节对象数组;
- 2、将其反序列化成布隆过滤器;
- 3、通过遍历检查查询关键词是否存在于布隆过滤器;
- 4、若存在则将密文解密并进行精确过滤(因为布隆过滤器存在误判,所以需要进行精确模糊过滤);
布隆过滤器优点是空间效率高,查询速度快;缺点是可能存在误判(但不会漏判)。
总结
安全性与性能是系统设计中最重要的非功能性需求之二,提升安全性必然会降低性能,提升性能必须得牺牲安全性,二者相互影响成为系统中的平衡点之一。故如何在安全性与性能之间取得平衡,需要根据具体需求和业务场景作出合理取舍。

7833



被折叠的 条评论
为什么被折叠?



