TDE : chiffrées sur disque, déchiffrées dans la SGA

L’utilisation de tables dans les tablespaces chiffrés avec transparent data encryption (aka TDE) ne fonctionne pas du tout comme la compression ! En effet, alors qu’avec la compression, les blocs compressés sont manipulés compressés dans le buffer cache, ce n’est pas le cas avec le chiffrement au niveau du tablespace ; les blocs chiffrés sur disques sont déchiffrés systématiquement dans la SGA. Cet article, met en évidence ce point.

Quelle différence cela fait-il ? Une grosse en fait… En effet, lorsque vous chiffrez des données au niveau d’une colonne, les données indexées ne peuvent pas être accédées dans un index avec un INDEX RANGE SCAN. Dans le cas du chiffrement d’un tablespace, au contraire, tous les algorithmes associés aux plans d’exécutions restent identiques…

Colonnes indexées et chiffrées

Pour commencer, créez une table avec une colonne chiffrée avec TDE. Vous pourrez vérifier facilement que votre base de données ne peut plus utiliser un INDEX RANGE SCAN :

connect demo/demo

drop table t2 purge;

create table t2 (
id varchar2(10) encrypt no salt,
text varchar2(1000)
);

insert into t2
(select 'ARKZ_'||lpad(to_char(rownum),3,'0'),
rpad('X',100, 'Y')
from dual
connect by level <=100);

create index t2_id on t2(id);

exec dbms_stats.gather_table_stats(user, 't2');

explain plan for
select /*+ index_asc(t2) */ count(*)
from t2
where id between 'ARKZ_001' and 'ARKZ_002';

select *
from table(dbms_xplan.display(format=>'basic +cost +predicate'));

PLAN_TABLE_OUTPUT
-----------------------------------------------------
Plan hash value: 3321871023

------------------------------------------------
| Id | Operation | Name | Cost (%CPU)|
------------------------------------------------
| 0 | SELECT STATEMENT | | 3 (0)|
| 1 | SORT AGGREGATE | | |
|* 2 | TABLE ACCESS FULL| T2 | 3 (0)|
------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

2 - filter(INTERNAL_FUNCTION("ID")>='ARKZ_001' AND
INTERNAL_FUNCTION("ID")<='ARKZ_002'
)

Tablespace chiffré

Si vous faites le même test avec une table stockée dans un tablespace chiffré avec TDE, le résultat est très différent :

create tablespace encrypted_tbs
datafile '/u01/app/oracle/oradata/BLACK/encrypted_tbs.dbf'
size 100M ENCRYPTION USING 'AES256'
DEFAULT STORAGE(ENCRYPT);

drop table t1 purge;

create table t1 (
id varchar2(10),
lib varchar2(1000))
tablespace encrypted_tbs;

insert into t1
(select 'ARKZ_'||lpad(to_char(rownum),3,'0'),
rpad('X',100, 'Y')
from dual
connect by level <=100);

create index t1_id on t1(id) tablespace encrypted_tbs;

exec dbms_stats.gather_table_stats(user, 't1');

explain plan for
select /*+ index_asc(t1) */ count(*)
from t1
where id between 'ARKZ_001' and 'ARKZ_002';

select *
from table(dbms_xplan.display(format=>'basic +cost +predicate'));

PLAN_TABLE_OUTPUT
------------------------------------------------
Plan hash value: 131123927

------------------------------------------------
| Id | Operation | Name | Cost (%CPU)|
------------------------------------------------
| 0 | SELECT STATEMENT | | 1 (0)|
| 1 | SORT AGGREGATE | | |
|* 2 | INDEX RANGE SCAN| T1_ID | 1 (0)|
------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("ID">='ARKZ_001' AND "ID"<='ARKZ_002')

Explications « in the blocks »

Une fois l’exemple créé, chargez le bloc d’index en mémoire et dumper son contenu en retrouvant l’adresse en mémoire dans x$bh et en utilisant oradebug peek ; vous constaterez que les données sont en clair dans la SGA mais qu’elle sont complètement chiffrées dans les tablespaces :

select rows_per_block, count(*) blocks
FROM (
SELECT /*+ index_ffs(t1 t1_id) */
sys_op_lbid(13802, 'L', rowid) block_id,
COUNT(*) rows_per_block
FROM T1
WHERE id IS NOT NULL
GROUP BY sys_op_lbid(13802, 'L', rowid))
GROUP BY rows_per_block;

ROWS_PER_BLOCK BLOCKS
-------------- ----------
100 1

select file_id, block_id, block_id+blocks
from dba_extents
where segment_name = 'T1_ID'
and owner=user;

FILE_ID BLOCK_ID BLOCK_ID+BLOCKS
------- ---------- ---------------
5 136 144

select ba, dbarfil, dbablk
from x$bh
where dbarfil = 5
and dbablk between 136 and 144;

BA DBARFIL DBABLK
---------------- ---------- ----------
00000000796D4000 5 136
00000000794B0000 5 138
00000000795C8000 5 137
00000000797B0000 5 139

oradebug setmypid
oradebug tracefile_name
/u01/app/oracle/diag/rdbms/black/BLACK/trace/BLACK_ora_4374.trc
oradebug peek 0x797B0000 8192 1


*** 2011-05-26 22:56:35.223
Processing Oradebug command 'peek 0x797B0000 8192 1'
[0797B0000, 0797B2000) = 0000A206 0140008B 0010CBAE 04010000 0000E7AF ...
Dump of memory from 0x0797B0014 to 0x0797B2000
0797B0010 00000002 000035EA 0010CBAC [.....5......]
0797B0020 00000000 00320002 01400088 00000000 [......2...@.....]
0797B0030 00000000 00000000 00000000 00000000 [................]
0797B0040 00000000 0000FFFF 00000000 00000000 [................]
0797B0050 00000000 00008000 0010CBAC 00000000 [................]
0797B0060 00000000 02800000 00000000 00EC0064 [............d...]
0797B0070 176C1858 00000000 00000000 00000000 [X.l.............]
0797B0080 00000000 00001F60 1F3C1F4E 1F181F2A [....`...N.<.*...]
0797B0090 1EF41F06 1ED01EE2 1EAC1EBE 1E881E9A [................]
0797B00A0 1E641E76 1E401E52 1E1C1E2E 1DF81E0A [v.d.R.@.........]
0797B00B0 1DD41DE6 1DB01DC2 1D8C1D9E 1D681D7A [............z.h.]
0797B00C0 1D441D56 1D201D32 1CFC1D0E 1CD81CEA [V.D.2. .........]
0797B00D0 1CB41CC6 1C901CA2 1C6C1C7E 1C481C5A [........~.l.Z.H.]
0797B00E0 1C241C36 1C001C12 1BDC1BEE 1BB81BCA [6.$.............]
0797B00F0 1B941BA6 1B701B82 1B4C1B5E 1B281B3A [......p.^.L.:.(.]
0797B0100 1B041B16 1AE01AF2 1ABC1ACE 1A981AAA [................]
0797B0110 1A741A86 1A501A62 1A2C1A3E 1A081A1A [..t.b.P.>.,.....]
0797B0120 19E419F6 19C019D2 199C19AE 1978198A [..............x.]
0797B0130 19541966 19301942 190C191E 18E818FA [f.T.B.0.........]
0797B0140 18C418D6 18A018B2 187C188E 1858186A [..........|.j.X.]
0797B0150 00000000 00000000 00000000 00000000 [................]
Repeat 373 times
0797B18B0 00000000 00000000 00000000 41080000 [...............A]
0797B18C0 5F5A4B52 06303031 84004001 00002400 [RKZ_100..@...$..]
0797B18D0 4B524108 39305F5A 40010639 23008400 [.ARKZ_099..@...#]
0797B18E0 41080000 5F5A4B52 06383930 84004001 [...ARKZ_098..@..]
0797B18F0 00002200 4B524108 39305F5A 40010637 [."...ARKZ_097..@]
0797B1900 21008400 41080000 5F5A4B52 06363930 [...!...ARKZ_096.]
[...]
0797B1F40 83004001 00000700 4B524108 30305F5A [.@.......ARKZ_00]
0797B1F50 40010637 06008300 41080000 5F5A4B52 [7..@.......ARKZ_]
0797B1F60 06363030 83004001 00000500 4B524108 [006..@.......ARK]
0797B1F70 30305F5A 40010635 04008300 41080000 [Z_005..@.......A]
0797B1F80 5F5A4B52 06343030 83004001 00000300 [RKZ_004..@......]
0797B1F90 4B524108 30305F5A 40010633 02008300 [.ARKZ_003..@....]
0797B1FA0 41080000 5F5A4B52 06323030 83004001 [...ARKZ_002..@..]
0797B1FB0 00000100 4B524108 30305F5A 40010631 [.....ARKZ_001..@]
0797B1FC0 00008300 00000000 00000000 00000000 [................]
0797B1FD0 00000000 00000000 00000000 00000000 [................]
Repeat 1 times
0797B1FF0 00000000 00000000 00000000 CBAE0601 [................]

Si on dump le bloc avec dd, on ne voit pas les données ni d’ailleurs les adresses des blocks de données :

dd if=/u01/app/oracle/oradata/BLACK/encrypted_tbs.dbf 
bs=8192 skip=139 count=1 of=greg.out

vi greg.out

[ESC]:!xxd
[ESC]:w greg.txt
[ESC]:q

cat greg.txt
0000000: 06c2 a200 00c2 8b00 4001 c2ae c38b 1000 ........@.......
0000010: 0000 0114 1f4e 0000 0cc3 9c05 c3bb 321b .....N........2.
0000020: c29f c392 c39a c294 76c2 88c2 acc2 bdc3 ........v.......
0000030: 8c40 c392 30c3 b6c2 90c3 a154 c287 c29a .@..0......T....
0000040: c3b1 376f c2ae c396 c2a2 c2af c2b8 c28d ..7o............
0000050: c39c 38c2 bec2 acc3 8bc2 a315 c28d c296 ..8.............
0000060: 2cc3 b512 71c3 8c7b c393 73c3 8ac3 89c3 ,...q..{..s.....
0000070: 97c3 96c3 aec3 b07c 67c2 b958 c39b c2bd .......|g..X....
0000080: 7830 c38b c2a2 1c33 c293 c3a9 14c3 a957 x0.....3.......W
0000090: c3a2 7ec2 8662 c3a4 c3bd 3cc3 bac2 ba7d ..~..b....<....}
00000a0: 3fc3 9e62 c2a6 c384 c3b2 c2b1 52c3 9167 ?..b........R..g
00000b0: 1ac3 b82a c3a7 7c61 7745 21c2 a740 c387 ...*..|awE!..@..
00000c0: 4cc3 83c2 aac3 aac2 914f c39a c395 c2a2 L........O......
00000d0: 70c2 9210 c3af 4504 34c3 adc2 80c3 9f73 p.....E.4......s
00000e0: c3a3 c38d 08c2 8f6a c3a8 0e43 c29c c38e .......j...C....
00000f0: c3ae 7e46 150e 3cc3 b7c2 a202 c393 3bc2 ..~F..<.......;.
0000100: 92c3 a8c3 b2c3 b142 10c3 9fc2 93c2 8c75 .......B.......u
0000110: 1e47 c3a3 3ac2 93c2 b506 c39d 19c2 8ac2 .G..:...........
0000120: 8740 67c2 afc3 b7c2 8211 67c2 a473 0b4d .@g.......g..s.M
0000130: 0f12 2ec3 915d 35c2 aac3 b537 53c3 90c2 .....]5....7S...
0000140: 9838 c2a6 c2af 2bc2 8a4d 07c3 aa12 c3b8 .8....+..M......
0000150: 22c2 9ac2 8566 3d46 c39a 4406 c29f 45c3 "....f=F..D...E.
0000160: ba12 c3a2 c38c 2c2e 14c3 94c3 a8c3 9cc2 ......,.........
0000170: a7c2 bdc3 9b6a 3632 c3b3 73c3 9704 c39a .....j62..s.....
0000180: 09c2 bd31 7cc2 a5c2 bf2f c2a9 c3bb 7d4a ...1|..../....}J
0000190: c3a5 4f1f c28a 3c5f c3ba c2a8 0d4a c388 ..O...<_.....J..
00001a0: 5e59 c39d 2c23 c3ba 5b66 c3ae 38c2 9bc3 ^Y..,#..[f..8...
00001b0: ae40 25c3 a3c2 9912 c299 7c60 c3af c284 .@%.......|`....
00001c0: c3ad 4b35 c38b 60c3 9307 c289 13c3 8340 ..K5..`........@
00001d0: 21c3 be39 c3ad c384 c3af 510f 0fc3 ae0b !..9......Q.....
[...]

Etant donné que la structure des blocs en mémoire est strictement identique à une structure de blocs dans un segment non chiffré, cela explique que les plans soient identiques.

Evidemment cela pose la question du fonctionnement avec Exadata puisque c’est l’instance qui chiffre/déchiffre les blocs. Et bien, alors que les storage servers de la version 1 ne pouvaient exploiter les données chiffrées que dans l’instance, la version 2 sait, non seulement les déchiffrer de manière transparente dans les storage servers. TDE sait, en outre, s’appuyer sur l’accélération matérielle des processeurs Intel® Xeon®L5640 des cellules Exadata.