Redacting data from the Oracle Database
Published on: Author: Bastiaan Bak Category: IT development and operationsOracle Data Redaction is a new feature in Oracle Database 12c. Redaction is the process of masking information from the database to applications. The new feature provides methods to mask sensitive data that is returned from queries.
Oracle Data Redaction uses the new package DBMS_REDACT that was introduced in Oracle database version 12c. This functionality was also backported to release 11.2.0.4. The package is used to define policies that control what data is shown to the application users.
For each policy you define:
- what table or column needs to be redacted
- under what conditions data needs to be redacted
- in what way the data needs to be redacted
This can be completely configured with PL/SQL code.
Types of redaction
Here are some examples of the different types of redaction:
Setup
To define a policy you need to have execute privileges on the DBMS_REDACT package. This package is used to add, alter or drop policies. These are the parameters for the DBMS_REDACT.ADD_POLICY procedure:
PROCEDURE ADD_POLICY Argument Name TYPE IN/OUT DEFAULT? ------------------------------ ----------------------- ------ -------- OBJECT_SCHEMA VARCHAR2 IN DEFAULT OBJECT_NAME VARCHAR2 IN POLICY_NAME VARCHAR2 IN POLICY_DESCRIPTION VARCHAR2 IN DEFAULT COLUMN_NAME VARCHAR2 IN DEFAULT COLUMN_DESCRIPTION VARCHAR2 IN DEFAULT FUNCTION_TYPE BINARY_INTEGER IN DEFAULT FUNCTION_PARAMETERS VARCHAR2 IN DEFAULT EXPRESSION VARCHAR2 IN ENABLE BOOLEAN IN DEFAULT REGEXP_PATTERN VARCHAR2 IN DEFAULT REGEXP_REPLACE_STRING VARCHAR2 IN DEFAULT REGEXP_POSITION BINARY_INTEGER IN DEFAULT REGEXP_OCCURRENCE BINARY_INTEGER IN DEFAULT REGEXP_MATCH_PARAMETER VARCHAR2 IN DEFAULT
Exceptions
If you have the EXEMPT REDACTION POLICY system privilege, data is never redacted. Redaction is also never used in the following situations:
- connections as SYS
- database replication
- backup and restore
- export and import
Test setup
For testing we’ve created a table, and inserted some data:
CREATE TABLE CUSTOMERS (cust_id NUMBER NOT NULL ,name varchar2(20) ,credit_card NUMBER(16) ,exp_date DATE ,creditlimit NUMBER(6,2) ,emailadr varchar2(20) ) / INSERT INTO CUSTOMERS VALUES (8714,'John Smith' ,6283723982745971,'01-FEB-2017',49.50,'john.smith@gmail.com'); INSERT INTO CUSTOMERS VALUES (8743,'Paul McCartney',8995984092832971,'01-APR-2017',150.00,'paul@oracle.com'); INSERT INTO CUSTOMERS VALUES (8345,'George Lennon' ,9345945634845567,'01-FEB-2017',19.99,'glennon@qualogy.com'); INSERT INTO CUSTOMERS VALUES (9019,'Ricky Star' ,2383834982340341,'01-JUN-2017',100.00,'star@aol.com');
This is how the data is displayed if no redaction is used:
ALTER SESSION SET NLS_DATE_FORMAT='DD-MON-YYYY'; COLUMN credit_card format 0000000000000000 COLUMN EMAILADR format a20 SELECT * FROM CUSTOMERS / CUST_ID NAME CREDIT_CARD EXP_DATE CREDITLIMIT EMAILADR ---------- -------------------- ----------------- ----------- ----------- -------------------- 8714 John Smith 6283723982745971 01-FEB-2017 49.5 john.smith@gmail.com 8743 Paul McCartney 8995984092832971 01-APR-2017 150 paul@oracle.com 8345 George Lennon 9345945634845567 01-FEB-2017 19.99 glennon@qualogy.com 9019 Ricky Star 2383834982340341 01-JUN-2017 100 star@aol.com
Different types of redaction
Within the policy definition we set the FUNCTION_TYPE that defines what kind of redaction is used:
- DBMS_REDACT.NONE
- DBMS_REDACT.FULL
- DBMS_REDACT.PARTIAL
- DBMS_REDACT.RANDOM
- DBMS_REDACT.REGEXP
Full redaction
The most basic form is the full redaction. Here we define a policy that hides the values from the credit card column.
The policy is defined by a user that has execute privileges on the DBMS_REDACT package, like the SYSTEM user. All queries are executed by the schema-owner TEST_REDACT.
In this example the creditcard data is always redacted, because the expression ‘1=1’ is always true.
BEGIN DBMS_REDACT.ADD_POLICY ( object_schema => 'TEST_REDACT', object_name => 'CUSTOMERS', column_name => 'CREDIT_CARD', policy_name => 'REDACT_CARD_POLICY', function_type => DBMS_REDACT.FULL, expression => '1=1' ); END; / CUST_ID NAME CREDIT_CARD EXP_DATE CREDITLIMIT EMAILADR ---------- -------------------- ----------------- --------- ----------- -------------------- 8714 John Smith 0000000000000000 01-FEB-17 49.5 john.smith@gmail.com 8743 Paul McCartney 0000000000000000 01-APR-17 150 paul@oracle.com 8345 George Lennon 0000000000000000 01-FEB-17 19.99 glennon@qualogy.com 9019 Ricky Star 0000000000000000 01-JUN-17 100 star@aol.com
Partial redaction
Here we mask position 1 to 12 of the credit card number with a zero:
BEGIN DBMS_REDACT.ADD_POLICY ( object_schema => 'TEST_REDACT', object_name => 'CUSTOMERS', column_name => 'CREDIT_CARD', policy_name => 'REDACT_CARD_POLICY', function_type => DBMS_REDACT.PARTIAL, function_parameters => '0,1,12', expression => '1=1' ); END; / CUST_ID NAME CREDIT_CARD EXP_DATE CREDITLIMIT EMAILADR ---------- -------------------- ----------------- --------- ----------- -------------------- 8714 John Smith 0000000000005971 01-FEB-17 49.5 john.smith@gmail.com 8743 Paul McCartney 0000000000002971 01-APR-17 150 paul@oracle.com 8345 George Lennon 0000000000005567 01-FEB-17 19.99 glennon@qualogy.com 9019 Ricky Star 0000000000000341 01-JUN-17 100 star@aol.com
Partial redaction can also be done on a date. Here we keep the year, but hide the rest of the date:
BEGIN DBMS_REDACT.ADD_POLICY ( object_schema => 'TEST_REDACT', object_name => 'CUSTOMERS', column_name => 'EXP_DATE', policy_name => 'REDACT_CARD_POLICY', function_type => DBMS_REDACT.PARTIAL, function_parameters => 'm1d1Y', expression => '1=1' ); END; / CUST_ID NAME CREDIT_CARD EXP_DATE CREDITLIMIT EMAILADR ---------- -------------------- ----------------- --------- ----------- -------------------- 8714 John Smith 6283723982745971 01-JAN-17 49.5 john.smith@gmail.com 8743 Paul McCartney 8995984092832971 01-JAN-17 150 paul@oracle.com 8345 George Lennon 9345945634845567 01-JAN-17 19.99 glennon@qualogy.com 9019 Ricky Star 2383834982340341 01-JAN-17 100 star@aol.com
Random redaction
With random redaction the information is replaced with other random data:
BEGIN DBMS_REDACT.ADD_POLICY ( object_schema => 'TEST_REDACT', object_name => 'CUSTOMERS', column_name => 'CREDIT_CARD', policy_name => 'REDACT_CARD_POLICY', function_type => DBMS_REDACT.RANDOM, expression => '1=1' ); END; / CUST_ID NAME CREDIT_CARD EXP_DATE CREDITLIMIT EMAILADR ---------- -------------------- ----------------- --------- ----------- -------------------- 8714 John Smith 1860716071681409 01-FEB-17 49.5 john.smith@gmail.com 8743 Paul McCartney 8744124307922429 01-APR-17 150 paul@oracle.com 8345 George Lennon 5901451110955182 01-FEB-17 19.99 glennon@qualogy.com 9019 Ricky Star 1901737681759520 01-JUN-17 100 star@aol.com
Redaction using regular expressions
With regular expression we can redact the data based on pattern matching. There are predefined patterns for a number of situations, like email addresses and IP addresses. Here we hide the beginning of the email address:
BEGIN DBMS_REDACT.ADD_POLICY ( object_schema => 'TEST_REDACT', object_name => 'CUSTOMERS', column_name => 'EMAILADR', policy_name => 'REDACT_CARD_POLICY', function_type => DBMS_REDACT.REGEXP, regexp_pattern => DBMS_REDACT.RE_PATTERN_EMAIL_ADDRESS, regexp_replace_string => DBMS_REDACT.RE_REDACT_EMAIL_NAME, regexp_position => DBMS_REDACT.RE_BEGINNING, regexp_occurrence => DBMS_REDACT.RE_ALL, expression => '1=1' ); END; / CUST_ID NAME CREDIT_CARD EXP_DATE CREDITLIMIT EMAILADR ---------- -------------------- ----------------- --------- ----------- -------------------- 8714 John Smith 6283723982745971 01-FEB-17 49.5 xxxx@gmail.com 8743 Paul McCartney 8995984092832971 01-APR-17 150 xxxx@oracle.com 8345 George Lennon 9345945634845567 01-FEB-17 19.99 xxxx@qualogy.com 9019 Ricky Star 2383834982340341 01-JUN-17 100 xxxx@aol.com
Expression
In the examples above the parameter value for expression was allways ‘1=1’, so the data is always redacted.
The packages gives you the option to redact the data based on the situation. A common way to do this is to use the SYSCONTEXT function, which gives you information about the current session. In this way you can redact your data based on the user, the current role or the used IP address.
To redact data only for user JOHN you can add this expression to the profile:
expression => 'SYS_CONTEXT(''USERENV'',''SESSION_USER'') = ''JOHN'''
Data dictionary views
There are some views that can be queried to display information about the defined policies:
SELECT OBJECT_OWNER , OBJECT_NAME , POLICY_NAME , EXPRESSION FROM REDACTION_POLICIES / OBJECT_OWNER OBJECT_NAME POLICY_NAME EXPRESSION ------------ ------------ -------------------- ------------ TEST_REDACT CUSTOMERS REDACT_CARD_POLICY 1=1 SELECT OBJECT_OWNER , OBJECT_NAME , COLUMN_NAME , FUNCTION_TYPE , FUNCTION_PARAMETERS FROM REDACTION_COLUMNS / OBJECT_OWNER OBJECT_NAME COLUMN_NAME FUNCTION_TYPE FUNCTION_PARAMETERS ------------ ------------ ------------ --------------------------- -------------------- TEST_REDACT CUSTOMERS CREDIT_CARD PARTIAL REDACTION 0,1,12
Conclusion
The Oracle Data Redaction feature makes it possible to hide sensitive information. The functionality only changes the way the data is displayed, there are no changes in the storing of the data.
With the DBMS_REDACT package the redaction policies can defined in a very flexible way.
More information
-
A full description of the possibilities of the profiles can be found here
-
Click here for a complete list of possible values for the USERENV parameters see the Oracle documentation
Hi,
How can we add multiple numbers of users in following:-
expression => 'SYS_CONTEXT(''USERENV'',''SESSION_USER'') IN (SELECT USERNAME FROM DBA_USERS)'
Hi Shailesh
You use "(SELECT USERNAME FROM DBA_USERS)" , so you want redaction for all users ?
Then you could use expression => '1=1'
A more realistic scenario would be to add redaction for users who don't have a specific role.
For that you can use something like :
expression => 'SYS_CONTEXT(''SYS_SESSION_ROLES'',''MGR'') = ''FALSE''');
Hi!
Would it be possible to mask only part of the returned rows? I mean, for example, those for people in some departments are masked and some other are shown, depending on a condition…
Thank you!
I need to redact the below users.
is there any option to use the 'IN ' keyword in the expression parameter in dbms_redact package?
list of users: ABC,XYZ,MNK
Expression =>'SYS_CONTEXT(''USERENV","SESSION_USERS") IN ("ABC","XYZ","MNK");