2. About Me
l Email:liu.maclean@gmail.com
l Blog:www.oracledatabase12g.com
l Oracle Certified Database Administrator Master 10g
and 11g
l Over 6 years experience with Oracle DBA technology
l Over 7 years experience with Linux technology
l Member Independent Oracle Users Group
l Member All China Oracle Users Group
l Presents for advanced Oracle topics: RAC,
DataGuard, Performance Tuning and Oracle Internal.
3. 在 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
4. 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 个字节的开销。
5. 另外 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
6. 列加密特性对于表连接(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
7. 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 */