作为应用最广泛的信息存储和处理系统,数据库中存在大量敏感数据,如何防止数据被窃取和篡改是重中之重。加密技术是提高数据库安全的一个重要手段,在对数据库中存储数据进行加密时,需要结合它们的特点,对加密算法、加密粒度以及加密方式进行合理选择。比如,在选择加密算法时,对加密尤其是解密速度要求比较快,不能因为加/解密过程而导致系统性能大幅度下降。其次,应当支持灵活的加密粒度。根据用户的需要,能够选择对数据库、表、记录、字段、数据项进行加密。同时,还应结合目前DBMS选择适当加密方式。

但不是什么加密方法和方式都是适合数据库的,在加密数据的同时,也会带来一些相关问题,如果处理不好,不仅会降低数据库的安全性,而且还会带来其它如性能的影响。

本文简要介绍和分析了在一个基于开源数据库PostgreSQL的安全数据库系统研究的基础上,如何针对PostgreSQL数据库的存储数据的不同层次,对数据库的文件(或表、页)、记录、字段等多个层次实现了加密。

数据库不同加密粒度实现

按照PostgreSQL数据库的结构层次,数据库的加密粒度可以分为相应的数据库级、表级、记录级、字段级和数据项级。根据不同的应用需要,选择合适的加密粒度。

一、对整个数据库加密

整个数据库加密,就是对数据库中所有的系统表、数据表、索引、视图和存储过程等进行加密处理。这种加密方法简单快捷,只需对相应数据库文件进行加密处理即可,对于企业或者用户简单的备份整个数据库,可以采取这种操作方便加密粒度。但是,使用中的数据库如果采用这种方法则会非常不合适,因为数据库中的数据共享性高,会同时被多个用户和应用访问使用,即使只需要查询一条记录,也需要对整个数据库进行解密,对系统性能会产生极大的影响。因此对此整个数据库加密的方式我们不作过多的研究和讨论。

二、 数据库的表级(页面)加密

安全数据库将表分成两大类,含加密字段的表和不含加密字段的表。对于加密的数据表则整个块加密,否则不加密,且每块使用一个密钥。安全数据库中,如果一个表中存放敏感数据,那么这个表的每一个页面的数据域在内存中是明文,在外存中是密文。

表级加密的对象是数据库中的表。与数据库级加密比较,采用表级加密粒度,系统的查询性能会有所改善,因为对于未加密表的查询,与传统查询方法一样,系统性能不会受到影响,对于加密表的查询,只需要解密对应的加密表,而不要解密整个数据库。在实行表级加密时,可以采用对存储数据的磁盘块(页面)进行加密。这种方法与DBMS集成时,需要对DBMS内部一些核心模块进行修改,包括对词法分析器、解释器和查询执行器的修改,这里我们对开放源代码的数据库管理系统PostgreSQL的源码进行修改。

1) 加解密数据结构

加密函数:

Char cipherpage(char *buffer,int size,BlockNumber blocknum, Relation reln);

解密函数:

Char decipherpage(char *buffer,int size,BlockNumber blocknum, Relation reln);

输入:

Buffer:加解密数据块所在内存缓冲区;

Size:数据块大小;

Blocknum:数据块定位信息,需要写入或读出的数据块;

Reln:记录数据库名,数据库关系表,数据空间的信息;

返回值:

加解密成功后,返回数据块所在的内存缓冲区。

2) 加解密流程的实现

当DBMS将内存中的页面写出到外存中时,首先查询安全字典,如果需要加密存储,则用安全字典中当前密钥加密。在将数据从外存读入内存后,如果页头标识页面是加密过的(由函数iscryptic()进行判断),则取出相应的密钥解密。加密时,只加密页面中的数据域。这里也是由iscryptic()对进行读写的关系表进行判断是否需要加解密,来对当前的数据块进行加密或解密。解密时,当数据块从外存读入内存时,如果页头标识页面是加密过的,则从安全字典中取出相应的密钥解密数据域。加解密的密钥通过主密钥和页块的ID计算得出。

我们跟踪数据的写入和读入流程,找到写入和读出数据块(block)的位置,对该数据块进行加解密。

和对整个数据库加密一样,对数据库进行表级加密实现方法简单,但是加密的粒度较粗,一张表中会含有大量不需要加密的数据,这样也会导致效率低下。

三、使用TOAST技术实现字段加密

使用TOAST技术,将加密的字段从原始的数据中分离出来,单独存放。按照此种处理方案,同一个表中的明文密文存放在不同的文件或页面中。

首先对TOAST (The Oversized-Attribute Storage Technique,超大尺寸字段存储技术)进行一个简单的概述。

PostgreSQL 数据库的页面大小是固定的(通常是 8Kb),并且不允许行跨越多个页面,因此不可能直接存储非常大的字段值。为了突破这个限制,大的字段值被压缩和/或打碎成多个物理行。(这些事情对用户都是透明的)。这个技术的昵称是 TOAST("切片面包之后最好的东西")。TOAST 代码只有在准备向某表中存储超过 BLCKSZ/4 字节(通常是 2KB)的行的时候才会触发。TOAST 代码将在对应于主表的TOAST表中压缩和/或另存字段值,直到数值比 BLCKSZ/4 字节短,或者无法得到更好的结果的时候才停止。

1)字段加密的实现

借助于TOAST技术我们可以将TOAST 代码的触发时机移植到当需要对某一数据库表的字段进行加密时。这样主表中需要加密的字段中只是存储实际已经加密的字段数据所在位置的索引值,实现的关键技术就是学习TOAST技术,这是实现的难点。

需要加密字段的数据在这里不是被分裂成(压缩后)固定字节的块后被存储到相应的TOAST表中,而是每个被加密后的字段数据块都作为独立的行在TOAST表里为所属主表存储。每个TOAST 表都有chunk_id字段(一个表示特定TOAST值的OID)、chunk_seq(一个序列号,存储该块在数值中的位置)、chunk_data(该块实际的数据)。在chunk_id 和chunk_seq上有一个唯一索引,提供对数值的快速检索。因此,一个表示线外 TOAST 值的指针数据需要存储要查阅的 TOAST 的OID和特定数值的OID(它的chunk_id)。这样在主表中依据这些标识就可以顺利找到被加密的字段,解密后即可使用。

TOAST 过的字段的大体积数值只是在把结果集发送给客户端的时候才抽出来。因此,主表要小得多,并且它的大部分行都存储在共享缓冲区里,因此就可以不需要任何线外存储。排序集也缩小了,并且排序将更多地在内存里完成。TOAST 表存储将近一半大小的裸数据,而主表只包含全部数据的 10%,这与在一个非 TOAST 的表对比起来,没有任何运行时的区别。因此我们不必担心TOAST带来性能上的影响。

与前面几种数据库加密方法不同,对数据库进行字段加密不需要对所有的数据进行加密,加密的粒度更小,对于不需要访问到的记录,完全不需要进行任何操作,所以使用起来效率会高一些。但是由于每一个字段都必须有一个密钥与之匹配,因此产生和管理记录密钥比较复杂。以记录为单位的加密分析与以字段为单位的加密情况相似。这里就不再进行叙述。

四、其他方式

在PostgreSQL数据库中,所有的记录都被构造成一个TUPLE。在构造的过程中,将加密列上的属性进行加密,保障内存中的数据是加密的,当一条记录被引用时根据TUPLE中的标识对其相应的字段进行解密。

本文基于PostgreSQL数据库自身特点简要讨论了几种数据库不同加密粒度式,用户根据应用场合的不同, 可分别选用以数据库表、记录、字段作为加密基本单位的方案。由于层次不同, 这三个方案的适用环境与实现难度各不相同。一般而言, 加密单位越小, 适用范围越广, 但实现时的困难也越大。而数据库的安全性仍然不断面临新的安全技术挑战,这有待我们去进一步解决。

知识点:

PostgreSQL是一个包含关系模型和支持SQL标准查询语言的DBMS(数据库管理系统)。