在Oracle 10g中出现了column encryption列加密特性,通过对列上的数据加密实现数据安全性的目的。当然实现这一加密特性是有代价的,一方面会导致所加密列数据每行所占磁盘空间字节数增长,另一方面会消耗更多的cpu和内存资源。 当使用Oracle的TDE(transparent data encryption)加密数据表的某一列时将导致该表上每行数据所占用的空间大致增加33-51个字节,这几十个字节用作以下用途:
  • 其中20个字节用以对加密值的完整性检查,该部分可以通过'nomac'选项来省略
  • 同时加密会填补加密值到16个字节(如果列本身长度不够的话),举例来说如果是9个字节长度的number类型,那么加密该number字段时就会需要将该数据填补到16个字节,也就是额外地多用了7个字节
  • 加密时默认采用salt选项(default),salt是指一串长度为16个字节的随机string,在数据被正式加密前这串string将会被添加到列上,这种做法使得***无法通过比对已知的密文来匹配加密值(steal patterns of ciphertext to known ciphertext);salt总是位于加密数据的末尾;该部分可以通过no salt选项来省略。
注意默认使用salt选项加密的列是不能创建索引的,所以强烈建议加密列时强制使用no salt选项!加密列上的索引不支持范围扫描操作(Range scans on encrypted columns can't use index),而加密表空间(encryption tablespace)没该限制。
SQL> create table enctab (t1 int encrypt);
Table created.

SQL>  create index ind_enc on enctab(t1);
create index ind_enc on enctab(t1)
*
ERROR at line 1:
ORA-28338: Column(s) cannot be both indexed and encrypted with salt

SQL> create table news (t1 varchar2(1) encrypt);
Table created.

/*以默认的salt和mac选项创建示例用表 */

SQL> insert into news values('1');
1 row created.

SQL> commit;
Commit complete.

/* 该列本身的长度为1个字节 */

SQL> select dump(t1,16) from news;

DUMP(T1,16)
--------------------------------------------------------------------------------
Typ=1 Len=1: 31

/* 以下为该行的dump信息,可以看到加密值增长到了52字节 */
col  0: [52]
de 76 08 74 2a c0 e3 94 89 e6 a8 3b 22 54 ca e5 af 4d eb a0 26 a7 e5 c2 f5
c0 e5 3a a0 09 9a 08 fa 56 2a 92 a0 83 b3 7f 0b 99 03 ad 12 78 d4 03 ec 6e
b3 c2
针对加密列可以使用'nomac'和no salt选项来减少性能损耗,其中'no mac'选项用以允许Oracle省略在加密数据中产生和存放message authentication code(MAC,信息验证代码),如上文所述的这部分代码用以对加密值的完整性检查,会占用20个字节的空间。通过使用'nomac'选项可以有效较少加密和后续操作所额外消耗的cpu周期,同时为加密值的存储减少20个字节的开销。 另外no salt选项用以省略加密中加入的16个字节的随机字符串(string),在能保证列值都唯一的情况下(***者无法通过已知密文比对的方式来解密),使用该选项可以有效减少cpu周期和每个单元16字节的空间开销。
oracledatabase12g.com>create table Maclean (t1 varchar2(16) encrypt no salt 'nomac');

/* 注意这里的nomac要被单引号括起来 */

oracledatabase12g.com>alter table table_name modify column_name encrypt [using ] [no salt] ['nomac'];
此外目前列加密不支持外键约束,造成这种限制的原因是每张表都有其唯一的密钥(encryption key);而表空间加密则不存在这种限制,即便某个从属表不在加密表空间上。
SQL> create table man (t1 int primary key );
Table created.

SQL> create table woman(t1 int encrypt);
Table created.

SQL>  alter table woman add constraint fk foreign key(t1) references man(t1);
 alter table woman add constraint fk foreign key(t1) references man(t1)
                                                 *
ERROR at line 1:
ORA-28335: referenced or referencing FK constraint column cannot be encrypted
列加密特性对于表连接(table joining)来说是透明的,即便作为连接条件的列被加密了也是如此(join tables is transparent,even if the columns for join condition are encrypted)。同时分区键是不能作为加密列的,否则将出现ORA-28346: an encrypted column cannot serve as a partitioning column错误。 此外加密列索引存在诸多限制,总结加密列索引(Indexes On Encrypted Columns)的几个restrictions:
  1. 只有使用no salt选项加密的列上才允许创建索引
  2. 加密列上不支持位图索引
  3. 加密列不支持外键
  4. 加密列上创建的索引只能做等式查询,因为不能做Range scan所以如between,like等非等式查询是不支持的;这种限制是由于索引中的数据也被加密了,所以实际上数据是以加密后的形式来排序的。所有非等式查询的条件均无法利用到索引,而使用全表扫描。
  5. 如果应用不使用等式查询的话,那么建议不要在加密列上创建索引,因为这样无益与性能,反而会增加性能开销。
而加密表空间(TDE Tablespace Encryption)不存在以上关于索引的限制,甚至在加密表空间上的表的索引在非加密表空间上也不会影响其使用,包括Range Scans;显然这一点出乎许多人的意料:
SQL> select tablespace_name,ENCRYPTED from dba_tablespaces  where tablespace_name in ('ENC','USERS');
TABLESPACE_NAME                ENC
------------------------------ ---
ENC                            YES
USERS                          NO

SQL> create table tv tablespace enc as select * from dba_objects;
Table created.

SQL> create index pk_tv on tv(object_id) tablespace enc;
Index created.

SQL> set autotrace on;
SQL> select 1 from tv where object_id=9999;
         1
----------
         1

Execution Plan
----------------------------------------------------------
Plan hash value: 2009574168
--------------------------------------------------------------------------
| Id  | Operation        | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT |       |     1 |    13 |     1   (0)| 00:00:01 |
|*  1 |  INDEX RANGE SCAN| PK_TV |     1 |    13 |     1   (0)| 00:00:01 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - access("OBJECT_ID"=9999)

/* 将该索引移动至非加密表空间上 */

SQL> alter index pk_tv rebuild tablespace users;
Index altered.

SQL> select 1 from tv where object_id=9999;

         1
----------
         1

Execution Plan
----------------------------------------------------------
Plan hash value: 2009574168
--------------------------------------------------------------------------
| Id  | Operation        | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT |       |     1 |    13 |     1   (0)| 00:00:01 |
|*  1 |  INDEX RANGE SCAN| PK_TV |     1 |    13 |     1   (0)| 00:00:01 |
--------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   1 - access("OBJECT_ID"=9999)

/* 可以看到虽然索引被移动到非加密表空间上但仍可以被利用到并range scan */

SQL> create bitmap index obj_typ on tv (object_type) tablespace users;
Index created.

/* 创建位图索引也没有问题 */
当然表空间加密也仍然存在一些限制:
  • 加密表空间不能使用传统的exp/imp工具导入导出,而只能使用datapump工具
  • 显然External Large Objects (BFILEs)这种存储在数据库外的外部大对象也不受支持。