Introduction à Oracle Data Redaction
Cet article a pour but de présenter, via un cas de test, l’utilisateur de Oracle Data Redaction, fonctionnalité présente depuis la version 11.2.0.4 et qui fait partie des fonctionnalités de l’option Advanced Security.
L’idée derrière cette fonctionnalité est de permettre la réécriture à la volée de certaines données critiques avant leur affichage, dans une perspective de confidentialité accrue. Cet article va s’intéresser à un cas concret,
la mise en place de restrictions sur l’affichage des numéros de cartes bancaires dans une table.
Préparation du cas de test
En tant que SYS, création d’un utilisateur de test :
CREATE USER EASYTEST IDENTIFIED BY secret; GRANT resource,connect to EASYTEST; GRANT UNLIMITED TABLESPACE TO EASYTEST;
Il faut à minima donner à l’utilisateur le droit d’exécution sur sys.dbms_redact pour qu’il puisse créer/modifier/supprimer des policies de rédaction :
GRANT EXECUTE ON sys.dbms_redact TO EASYTEST; GRANT SELECT ON sys.redaction_policies TO EASYTEST; GRANT SELECT ON sys.redaction_columns TO EASYTEST; GRANT SELECT ON sys.redaction_values_for_type_full TO EASYTEST;
Création de la table CLIENT pouvant stocker les numéros de cartes des clients dans un champ VARCHAR2 sous la forme « XXXX XXXX XXXX XXXX » :
connect EASYTEST; CREATE TABLE CLIENTS ( id number PRIMARY KEY, nom varchar2(128), prenom varchar2(128), num_cb varchar2(20), code_securite number(3), expiration date );
Alimentation de la table avec un jeu de données :
INSERT INTO CLIENTS VALUES (1, 'Brian', 'Charles','4556 5312 7506 7894','469',TO_DATE(TRUNC(DBMS_RANDOM.VALUE(TO_CHAR(DATE '2018-01-01','J'),TO_CHAR(DATE '2022-12-31','J'))),'J')); INSERT INTO CLIENTS VALUES (2, 'Vallet', 'Emmanuelle','4916 4540 7789 3521','954',TO_DATE(TRUNC(DBMS_RANDOM.VALUE(TO_CHAR(DATE '2018-01-01','J'),TO_CHAR(DATE '2022-12-31','J'))),'J')); INSERT INTO CLIENTS VALUES (3, 'Achin','Fabrice','4024 0071 0735 1941','422',TO_DATE(TRUNC(DBMS_RANDOM.VALUE(TO_CHAR(DATE '2018-01-01','J'),TO_CHAR(DATE '2022-12-31','J'))),'J')); INSERT INTO CLIENTS VALUES (4, 'Lampron','Paul','4716 0106 2214 4570','154',TO_DATE(TRUNC(DBMS_RANDOM.VALUE(TO_CHAR(DATE '2018-01-01','J'),TO_CHAR(DATE '2022-12-31','J'))),'J')); INSERT INTO CLIENTS VALUES (5, 'Alfred','Sylvain','4024 0071 6531 9970','843',TO_DATE(TRUNC(DBMS_RANDOM.VALUE(TO_CHAR(DATE '2018-01-01','J'),TO_CHAR(DATE '2022-12-31','J'))),'J')); commit;
Mise en place de policies DBMS_REDACT
A ce stade, sans surprise, le champs NUM_CB est visible dans la table lorsqu’on la requête :
set line 300; col nom for a15; col prenom for a15; col NUM_CB for a20; select * from easytest.clients; ID NOM PRENOM NUM_CB CODE_SECURITE EXPIRATIO -- --------------- --------------- -------------------- ------------- --------- 1 Brian Charles 4556 5312 7506 7894 469 18-DEC-18 2 Vallet Emmanuelle 4916 4540 7789 3521 954 19-NOV-21 3 Achin Fabrice 4024 0071 0735 1941 422 01-NOV-20 4 Lampron Paul 4716 0106 2214 4570 154 19-JUN-22 5 Alfred Sylvain 4024 0071 6531 9970 843 29-APR-20
Admettons désormais que l’on souhaite que le champ soit masqué lors d’un SELECT sur la table. Pour ce faire, il faut mettre en place une politique DBMS_REDACT qui s’appuie sur la fonction DBMS_REDACT.full, comme suit :
BEGIN DBMS_REDACT.add_policy( object_schema => 'easytest', -- Nom du schéma cible object_name => 'clients', -- Nom de la table cible column_name => 'num_cb', -- Nom du champs cible policy_name => 'redact_num_cc', -- Nom de la policy que l'on souhaite créer function_type => DBMS_REDACT.full, -- Fonction à utiliser dans cette policy expression => '1=1' -- Règle d'application, ici, 1=1 donc toujours active ); END; /
Si l’on requête à nouveau la table, on s’aperçoit que le champs NUM_CB ne s’affiche plus, il est masqué par la nouvelle policy :
select * from easytest.clients; ID NOM PRENOM NUM_CB CODE_SECURITE EXPIRATIO ---------- --------------- --------------- -------------------- ------------- --------- 1 Brian Charles 469 13-DEC-20 2 Vallet Emmanuelle 954 28-SEP-19 3 Achin Fabrice 422 12-SEP-18 4 Lampron Paul 154 01-JAN-19 5 Alfred Sylvain 843 18-MAR-20
Il est possible d’afficher les policies actuellement en place :
col OBJECT_OWNER for a15; col OBJECT_NAME for a15; col EXPRESSION for a40; col POLICY_DESCRIPTION for a60; SELECT * FROM sys.redaction_policies; OBJECT_OWNER OBJECT_NAME POLICY_NAME EXPRESSION ENABLE POLICY_DESCRIPTION --------------- --------------- ------------------------------ ---------------------------------------- ------- ------------------ EASYTEST CLIENTS redact_num_cc 1=1 YES
Ici, il s’agit d’une politique DBMS_REDACT.full, qui cache le champ dans son intégralité. Il est également possible de définir une policy de type DBMS_REDACT.partial qui peut cacher seulement une partie du champ.
Imaginons maintenant que l’on souhaite masquer seulement les 3 premiers groupes de digits du numéro de carte en les remplaçant par des astérisques et avec un affichage du numéro de carte au format « ****-****-****-XXXX »
BEGIN DBMS_REDACT.drop_policy ( object_schema => 'easytest', object_name => 'clients', policy_name => 'redact_num_cc' ); END; / BEGIN DBMS_REDACT.add_policy( object_schema => 'EASYTEST', object_name => 'CLIENTS', column_name => 'NUM_CB', policy_name => 'redact_num_cc', function_type => DBMS_REDACT.partial, expression => '1=1', function_parameters => 'VVVVFVVVVFVVVVFVVVV,VVVV-VVVV-VVVV-VVVV,*,1,12' ); END; /
Plutôt que supprimer/recréer la policy, il est également possible de la modifier. Aussi, on aurait pu arriver au même résultat avec :
BEGIN DBMS_REDACT.alter_policy ( object_schema => 'EASYTEST', object_name => 'CLIENTS', column_name => 'NUM_CB', policy_name => 'redact_num_cc', function_type => DBMS_REDACT.partial, action => DBMS_REDACT.MODIFY_COLUMN, expression => '1=1', function_parameters => 'VVVVFVVVVFVVVVFVVVV,VVVV-VVVV-VVVV-VVVV,*,1,12' ); END; /
Noter le paramètre supplémentaire action => DBMS_REDACT.MODIFY_COLUMN qui est nécessaire dans le cas d’une modification de policy. Le paramètre function_parameters, lui, permet de définir les règles de réécriture :
- VVVVFVVVVFVVVVFVVVV : Format d’entrée. Les « V » représente des caractères de valeur, soit les digits de notre code de carte. Les « F » des caractères de formatage, soit ici les espaces entre chaque bloc de 4 caractères.
- VVVV-VVVV-VVVV-VVVV : Format de sortie. Les « V » représente des caractères de valeur. Entre chaque bloc de 4 caractères, on utilise ici des tirets (-)
- * : Caractère de remplacement pour les caractères masqués
- 1 : Caractère de début du masquage
- 12 : Nombre de caractères masqués à partir du caractère de début
Une requête sur la table retourne le champ NUM_CB au format demandé :
select * from easytest.clients; ID NOM PRENOM NUM_CB CODE_SECURITE EXPIRATIO ---------- --------------- --------------- -------------------- ------------- --------- 1 Brian Charles ****-****-****-7894 469 13-DEC-20 2 Vallet Emmanuelle ****-****-****-3521 954 28-SEP-19 3 Achin Fabrice ****-****-****-1941 422 12-SEP-18 4 Lampron Paul ****-****-****-4570 154 01-JAN-19 5 Alfred Sylvain ****-****-****-9970 843 18-MAR-20
On peut également imaginer avoir besoin de masquer les numéros de carte uniquement lors de l’accès depuis un autre utilisateur Oracle. On peut spécifier via le paramètre « expression » une condition d’application de la règle. Si l’on veut que seul le user EASYTEST puisse lire les numéro de carte, il convient de modifier la policy comme suit :
BEGIN DBMS_REDACT.alter_policy ( object_schema => 'EASYTEST', object_name => 'CLIENTS', column_name => 'NUM_CB', policy_name => 'redact_num_cc', function_type => DBMS_REDACT.partial, action => DBMS_REDACT.MODIFY_COLUMN, expression => 'SYS_CONTEXT(''USERENV'',''SESSION_USER'') != ''EASYTEST''', function_parameters => 'VVVVFVVVVFVVVVFVVVV,VVVV-VVVV-VVVV-VVVV,*,1,12' ); END; /
Seul EASYTEST et les utilisateurs disposant du privilège EXEMPT REDACTION POLICY (typiquement, SYS) peuvent afficher les numéros de carte avec cette policy en place :
show user; USER is "EASYTEST" select * from easytest.clients; ID NOM PRENOM NUM_CB CODE_SECURITE EXPIRATIO ---------- --------------- --------------- -------------------- ------------- --------- 1 Brian Charles 4556 5312 7506 7894 469 13-DEC-20 2 Vallet Emmanuelle 4916 4540 7789 3521 954 28-SEP-19 3 Achin Fabrice 4024 0071 0735 1941 422 12-SEP-18 4 Lampron Paul 4716 0106 2214 4570 154 01-JAN-19 5 Alfred Sylvain 4024 0071 6531 9970 843 18-MAR-20
— > EASYTEST est l’exception dans la clause « expression » : le champ n’est pas réécrit
connect / as sysdba show user; USER is "SYS" select * from easytest.clients; ID NOM PRENOM NUM_CB CODE_SECURITE EXPIRATIO ---------- --------------- --------------- -------------------- ------------- --------- 1 Brian Charles 4556 5312 7506 7894 469 13-DEC-20 2 Vallet Emmanuelle 4916 4540 7789 3521 954 28-SEP-19 3 Achin Fabrice 4024 0071 0735 1941 422 12-SEP-18 4 Lampron Paul 4716 0106 2214 4570 154 01-JAN-19 5 Alfred Sylvain 4024 0071 6531 9970 843 18-MAR-20
— > SYS dispose du privilège EXEMPT REDACTION POLICY : le champ n’est pas réécrit
-- Création d'un nouvel utilisateur pour tester la réécriture. create user easytest_2 identified by secret; grant resource,connect to secrest; grant select on easytest.clients to easytest2; connect easytest2; select * from easytest.clients; ID NOM PRENOM NUM_CB CODE_SECURITE EXPIRATIO ---------- --------------- --------------- -------------------- ------------- --------- 1 Brian Charles ****-****-****-7894 469 13-DEC-20 2 Vallet Emmanuelle ****-****-****-3521 954 28-SEP-19 3 Achin Fabrice ****-****-****-1941 422 12-SEP-18 4 Lampron Paul ****-****-****-4570 154 01-JAN-19 5 Alfred Sylvain ****-****-****-9970 843 18-MAR-20
— > Bien que disposant du droit de lecture sur la table, le champ NUM_CB est réecrit pour l’utilisateur EASYTEST2.
Cette policy améliore la sécurité de la table en ne permettant pas l’affichage des numéros de carte malgré le privilège SELECT sur la table, et ce, à l’exception du proprietaire de la table. Le cas de test est basique, mais la fonctionnalité est très souple et permet une gestion très fine des politiques de sécurité.
Voir la documentation de Oracle Data Redaction pour appréhender les sujets plus complexes.
Nettoyage du cas de test
En tant que SYS :
BEGIN DBMS_REDACT.drop_policy ( object_schema => 'easytest', object_name => 'clients', policy_name => 'redact_num_cc' ); END; / drop user EASYTEST cascade; drop user EASYTEST2 cascade;