这个系列主要介绍了Oracle10gR2开始推出的透明加密(Transparent Data Encryption,简称TDE)。在上面一篇中,我们简单介绍了TDE的基本原理,以及如何配置使用TDE技术加密数据库中的数据。
TDE是在数据库层次进行数据保护加密的方法。存放在数据库中的数据都是经过加密处理,而加密密钥Key是分别存放在数据库内和独立数据库存储的。这样带来的好处是,一旦数据文件、硬盘丢失,非法获取到的数据内容是不可读的,实现了敏感数据加密。另一方面,数据在存入数据库、从数据库中读取的过程中,由数据库自动进行加解密处理,不需要开发人员额外的工作。实现了一定意义上的透明。
在本篇中,我们继续讨论TDE的加密处理机制在存储方面的效率,以及加入TDE后,对DML等操作带来的影响。最后,想从管理和开发的角度谈TDE带给应用的机遇与挑战。
TDE存储结构
在使用TDE的时候,通过指定数据列是否加密,来对数据进行透明的加解密处理。插入、修改和查询数据的时候,使用的都是明文信息。但是,保存在数据库文件中的内容,的确是加密过的内容。那么,在存储空间的使用上,加密处理过的数据是不是占据更少的空间呢。我们继续实验。
首先,我们创建两个相同条件的数据表。字段名称、类型全部一致,区别在于一个进行加密设置,而另一个不进行设置。
//建立数据表t
SQL> create table t
2 (id number encrypt no salt primary key); //t的数据列id是加密列
Table created
//数据表t_no
SQL> create table t_no
2 (id number primary key); //一般数据列id
Table created
建立两个数据表的作用在于对比,之后插入相同的数据信息。
//插入数据
SQL> insert into t values (10);
1 row inserted
SQL> insert into t_no values (10);
1 row inserted
SQL> commit;
Commit complete
数据已经插入,按照Oracle数据段分配原则:一个对象创建之后,默认分配一个区extents,包括八个数据块block。我们选择的数据比较简单,所以必然在一个数据块中。下面对数据块进行定位。
SQL> select dbms_rowid.rowid_relative_fno(rowid) fno,
2 dbms_rowid.rowid_block_number(rowid) block#
3 from t;
FNO BLOCK#
---------- ----------
4 5120
使用dbms_rowid包,可以根据rowid的信息定位到该行所在数据块的信息。rowid包括对象号+数据文件号+数据块号+行号(slot编号)。上面说明加密表t的一条记录在第四号文件中的5120块中。
接下来尝试将数据块导出dump处理。
//在有alter system权限的情况下,dump出指定的数据块信息;
SQL> alter system dump datafile 4 block 5120;
System altered
//获取跟踪文件位置,f_get_trace_name是自定义函数,用于获取trace文件位置;
SQL> select f_get_trace_name from dual;
F_GET_TRACE_NAME
--------------------------------------------------------------------------------
C:\TOOL\ORACLE\ORACLE\PRODUCT\10.2.0\ADMIN\OTS\UDUMP\ots_ora_7512.trc
获取到的trace文件,包括一个数据块的信息内容。其中,我们关注片段如下:
data_block_dump,data header at 0x8b08464
===============
tsiz: 0x1f98
hsiz: 0x14
pbl: 0x08b08464
bdba: 0x01001400
76543210
flag=--------
ntab=1
nrow=1
frre=-1
fsbo=0x14
fseo=0x1f70
avsp=0x1f5c
tosp=0x1f5c
0xe:pti[0] nrow=1 offs=0
0x12:pri[0] offs=0x1f70
block_row_dump:
tab 0, row 0, @0x1f70
tl: 40 fb: --H-FL-- lb: 0x1 cc: 1
col 0: [36]
ee 20 fa 3c ed 79 0a 91 d6 6f 68 90 43 1d 69 36 13 d9 cf c8 e3 3e 3a 56 66
01 de 9b 21 69 5e 8a 66 99 bf 52
end_of_block_dump
End dump data blocks tsn: 4 file#: 4 minblk 5120 maxblk 5120
数据行row 0,第一个col 0的取值,是一个较长的字段,不能区分出取值10(插入数据值10)。
为了对比,我们再看一下数据表t_no的情况。
SQL> select dbms_rowid.rowid_relative_fno(rowid) fno,
2 dbms_rowid.rowid_block_number(rowid) block#
3 from scott.t_no;
FNO BLOCK#
---------- ----------
4 5392
说明:数据表t_no的行,在文件4号的数据块5392上。
SQL> alter system dump datafile 4 block 5392;
System altered
SQL> select f_get_trace_name from dual;
F_GET_TRACE_NAME
--------------------------------------------------------------------------------
C:\TOOL\ORACLE\ORACLE\PRODUCT\10.2.0\ADMIN\OTS\UDUMP\ots_ora_4372.trc
dump出的数据内容为:
data_block_dump,data header at 0x86e8464
===============
tsiz: 0x1f98
hsiz: 0x14
pbl: 0x086e8464
bdba: 0x01001510
76543210
flag=--------
ntab=1
nrow=1
frre=-1
fsbo=0x14
fseo=0x1f92
avsp=0x1f7b
tosp=0x1f7b
0xe:pti[0] nrow=1 offs=0
0x12:pri[0] offs=0x1f92
block_row_dump:
tab 0, row 0, @0x1f92
tl: 6 fb: --H-FL-- lb: 0x1 cc: 1
col 0: [ 2] c1 0b
end_of_block_dump
End dump data blocks tsn: 4 file#: 4 minblk 5392 maxblk 5392
同样一行数据,在相同内容下,没有加过密的数据相对比较小。只有一个行开始标记,和直接的数据值。
结论:使用TDE的情况下,数据库文件中保存的数据值是进行过加密的。加密过的列值一般要长于原始数据值,所以使用TDE之后数据表要比不使用大。
操作的性能损耗
TDE的加解密操作完全是建立在自动加解密基础上。插入数据、修改数据的时候会自动将数据加密后存放在数据表中;选择数据时会自动的将加密过的列值进行解密。
这种操作无形中是增加了数据操作的成本,那么增加比例如何?
首先我们看插入操作实验。
//插入
SQL> create table t
2 (id number encrypt);
Table created
Executed in 0.062 seconds
SQL> select * from scott.t;
ID
----------
Executed in 0.016 seconds
//插入操作脚本
declare
i number;
begin
for i in 1..10 loop
insert into scott.t
select object_id from dba_objects;
if (mod(i,2)=0) then
commit;
end if;
end loop;
commit;
end;
/
SQL>
PL/SQL procedure successfully completed
Executed in 14.914 seconds
SQL> select count(*) from scott.t;
COUNT(*)
----------
527990
Executed in 0.109 seconds
对只有一个加密字段的数据表t,插入超过52万条数据,使用了近15s的时间。
如果不使用加密功能,性能如下:
SQL> create table t_no
2 (id number);
Table created
Executed in 0.031 seconds
//插入脚本结构与上述相同,此处略
SQL>
PL/SQL procedure successfully completed
Executed in 5.741 seconds
SQL> select count(*) from scott.t_no;
COUNT(*)
----------
527995
Executed in 0.062 seconds
对比之后,很容易发现两者的差异,同样的数据表结构(加密列除外),同样的脚本,同样的数据量。不使用TDE的时间只有不到6s,相当于使用TDE的三分之一。
使用筛选时的情况如下:
//搜索使用了加密列的数据表
SQL> select * from scott.t where id=1000;
ID
----------
1000
…
10 rows selected
Executed in 3.51 seconds
//未使用加密列的数据表搜索
SQL> select * from scott.t_no where id=1000;
ID
----------
1000
…
10 rows selected
Executed in 0.14 seconds
差异更加明显,在使用id作为条件的搜索方法中。使用TDE的搜索执行时间约4%,这也说明了使用ID进行查询条件的时候,解析条件还需要额外的成本。
在Oracle的官方文档中,对于使用TDE的性能问题有所涉及,认为带来的损耗是可以接受的。一些文献中给出的经验值也认为损耗在20%-30%之间。但综合上面两个实验:使用TDE,起码在目前的版本中,还是会有比较大的性能问题。这是进行技术方案选型的一个重要方面考量。
TDE管理和开发
从目前的情况看,TDE的主要应用是在特定的数据列和特定的表空间(对表空间TDE的使用请参考官方文档教程)。在系列的两篇中,我们一直在强调TDE的两个大优势:其一是数据层面加密,加密密钥与数据文件分开,增加了保险系数。另一方面,是透明加密,无需直接的管理。
这也就是说,使用TDE只是保证了数据在数据库中安全的,而且在wallet打开的情况下,使用sql语句查询实际上是没有限制的。所以,笔者认为TDE的这种优势在运维层面上的意义是大于开发上面的意义,起码可以很快让应用中的数据库层面实现加密,无需应用层面的支持。
此外,TDE是不负责数据传输阶段加密的。数据从DBMS传出后,还是以明文方式传输到应用。实际上,还是需要使用安全传输的解决方案。
运维方面,要注意数据库外层主密钥key的保护,如果损坏或者丢失,造成损失的几率还是很高的。
综上所述:TDE是一个数据库层级的加解密解决方案。在无法修改应用、又需要对数据库明文保存的敏感信息变成加密过信息的需要下是很有效的、快速的方案。但是,考虑到TDE自身的局限,以及带来的性能瓶颈损耗,在系统架构方案设计和解决方案选择的阶段还是尽量避免使用。