Sunday 10 February 2019

ORA-40365: The SYS user cannot be locked while the password file is in its current format.

CMD > orapwd file=$ORACLE_HOME/database/pwd$ORACLE_SID.ora format=12.2
CMD > orapwd describe file=pwd$ORACLE_SID.ora
Password file Description : format=12  
 
CMD > sqlplus / as sysdba
 
SQL> show user

USER is "SYS"

SQL> alter user sys account lock;

User altered.

SQL>  select username,lock_date from dba_users where username='SYS’:

USERNAME     LOCK_DATE

-------------------- ------------------

SYS     25-JAN-18

SQL> select username,lock_date from v$pwfile_users;

USERNAME     LOCK_DATE

-------------------- ------------------
SYS     25-JAN-18
SYSDG
SYSBACKUP
SYSKM 

Wednesday 6 February 2019

Oracle database firewall

It all started with acquisition of Secerno, a database firewall vendor, in 2010. Secerno's product “DataWall” helped analyze how databases are accessed so that DBA’s can set up policies to control the access.
The database firewall has the ability to analyze SQL statements sent from database clients and determine whether to pass, block, log, alert, or substitute SQL statements, based on a defined policy. Users can set whitelist and blacklist policies to control the firewall. It can detect injected SQL’s and block them.  According to Oracle, the database firewall can do the following -
  • Monitors and blocks SQL traffic on the network with white list, black list and exception list policies
  • Protects against application bypass, SQL injection and similar threats
  • Reports on database activity for SOX, PCI, HIPAA and other regulations, choosing from dozens of out-of-the-box reports
  • Supports other Databases as well - MS SQL Server, IBM DB2 , and Sybase
The Database Firewall joins other database-security products offered by Oracle such as Oracle Advanced Security, Audit Vault, Database Vault, Secure backup etc.
Oracle Database firewall comes in 2 components:-
Database Firewall:
  • Record and analyze SQL transaction requests and responses from one or more Oracle, Microsoft SQL Server, or Sybase databases, and Sybase SQL Anywhere.
  • Categorizes SQL transactions
  • Enforces data policies
  • Enables real-time alerting and event propagation
Database Firewall Management Server:
  • Aggregates SQL data from one or more Database Firewalls
  • Serves as a reporting platform for business reports
  • Centralizes the distribution of data control policies, but still enables the use of different policies for specific databases
  • Stores and manages log files, including archiving and restoring the log files
  • Remotely manages all Database Firewalls to which it connects
  • Integrates with third-party applications, such as Crystal Reports
However there are some key issues that it does not address and hence would need use of other security options such as Audit Vault, VPD etc. For example, Privileged users can login to the OS directly and make local connections to the database. This bypasses the database firewall.

Oracle Database Privileges

In the previous blog, we looked at the three types of privileges Oracle database provide - administrative, system and object privileges. We are taking a deeper dive on SYS and SYSDBA in this post as many users do not really understand the concept behind these entities. Based on our years of experience, in order to secure your database, you need to understand these concepts inside out. It is surprising to find that many stakeholders of the database still misunderstand these terms. We try to answer most frequently asked questions with respect to these terms.

Let's get this straight. SYS and SYSDBA are two different things! Figure 1 captures the difference.

Figure 1: SYS user and SYSDBA privilege

- SYS is a real database user. It is not just a real user, it is the super user of database - equivalent to Linux root user - it sees all and can do all in the database!
- SYSDBA is a privilege - it is an administrative privilege which allows to do virtually anything on the database (Figure 2 shows the operations a SYSDBA privileged user can do.)
Figure 2: What a SYSDBA can do

- SYS schema holds the data dictionary!
- SYS is by default granted SYSDBA privilege (no one can change this.) in addition to SYSOPER privilege.

How SYSDBA privilege is different from system/object privileges?
- Even though SYSDBA privilege is granted to a user, it is not exercise, if the does not login as SYSDBA
- CONNECT command decides if you are login as an administrative user or not (Figure 3)
Figure 3: Exercising SYSDBA privilege

- If you are granted system and/or object privileges, they are always available for you to exercise when you login to the database.
- Also, SYSDBA privilege allows you to operate on unopened database whereas system/object privileges can only be exercised in an open database.

Let's assume that user scott is granted SYSDBA privilege. He can either login as a regular database user in which case he is restricted to exercise only those operations that are granted through system/object privileges and roles. Note: if you are using the multi-tenant option, you will have to provide the PDB name in the CONNECT statement (say PDB name is pdb1, then CONNECT scott/tiger@pdb1). For simplicity we assume a non-multitenant database in this post.

SQL> CONNECT scott/tiger
Connected.
SQL> SHOW USER;
USER is "SCOTT"

Or he can login as SYSDBA. This allows him to exercise SYSDBA privileges in addition to other privileges he is granted via system/object privileges and roles.

SQL> CONNECT scott/tiger AS SYSDBA
Connected.
SQL> SHOW USER;
USER is "SYS"

- You can login to the database even when it is not open (i.e. only mounted)

What is the difference between SYS and SYSDBA privileged granted user (we call it a named SYSDBA)?
- When you login as SYS user, login user and current user are SYS.
- When you login as a named SYSDBA user, login user is the named user (e.g. scott) and the current user is SYS.
- SYS can revoke the SYSDBA privilege from the name SYSDBA user but the reverse is not true.

What can a named SYSDBA do in the database?
- The previous answer partially answers this question.
- A named SYSDBA acts like a SYS user. It can virtually do anything that SYS user can do including changing the password of SYS user. (A side note: relying on password to authenicate users is thus not a good idea! it is better to use external authentication mechanisms such as Kerberos.)

Do you ever need to login as SYSDBA user to maintain a database? What are the best practices?
Figure 4: Minimize the use of SYSDBA

- It is a good practice to minimize the use of SYS user account as much as possible.
- Most of the database operations do not need SYSDBA privileged users. However, you need to login as a SYSDBA user if you need to do maintenance operations such as patching, migration.
- If the database administrator does not need access to data but still need to perform maintenance operations, you may grant SYSOPER privilege instead.
- Also it is a good practice to use custom administrative privileges depending on the task at hand - SYSDG, SYSKM, SYSBACKUP and SYSRAC.
- If the database administrator is responsible for performing operations on an open database, you may not need to grant any administrative privileges to the administrator - only system and object privileges should be sufficient to do her job.

Who can grant SYSDBA privilege?
Figure 5: Granting SYSDBA privilege

- SYS
- Any user having SYSDBA privilege

Who can access SYS schema (i.e. data dictionary)?
Figure 6: Restricting Access to Data Dictionary

- SYS user
- If O7_DICTIONARY_ACCESSIBILITY parameter is set to FALSE, "ANY PRIVILEGE" users can access SYS schema objects. It is highly recommended to set this parameter to TRUE so that ANY PRIVILEGE users cannot access SYS schema data.
- Any user who is granted SELECT ANY DICTIONARY privilege.
- Any user given explicit object privilege on SYS schema objects
- Any user having SELECT_CATALOG_ROLE may select a designated set of SYS schema objects - the set of objects are decided by Oracle at the time of installation. (SELECT_CATALOG_ROLE is basically granted SELECT on a bunch of SYS objects - mainly views. There two similar roles EXECUTE_CATALOG_ROLE and DELETE_CATALOG_ROLE for execute and delete operations respectively.)

E.g.
Let's see the value of O7_DICTIONARY_ACCESSIBILITY parameter:
SQL> CONNECT / AS SYSDBA
Connected.
SQL> SHOW PARAMETER O7_DICTIONARY_ACCESSIBILITY;

NAME                                 TYPE        VALUE
------------------------------------ ----------- -----
O7_DICTIONARY_ACCESSIBILITY          boolean     FALSE

So, by default no one except SYS can access the data dictionary.

scott does not have access to dictionary table sys.user$.
SQL> CONNECT scott/tiger
Connected.
SQL> SELECT count(*) FROM sys.user$;
SELECT count(*) FROM sys.user$
                         *
ERROR at line 1:
ORA-00942: table or view does not exist

Let's check if scott has SELECT ANY TABLE privilege.
SQL> SELECT privilege FROM USER_SYS_PRIVS;
It shows that scott does not have SELECT ANY TABLE privilege

Let's grant SELECT ANY TABLE privilege to scott and see if he can access sys.user$.
SQL> CONNECT scott/tiger
Connected.
SQL> SELECT count(*) FROM sys.user$;
SELECT count(*) FROM sys.user$
                         *
ERROR at line 1:
ORA-00942: table or view does not exist
As you can see, O7_DICTIONARY_ACCESSIBILITY parameter blocks even powerful SELECT ANY TABLE privileged users.
The following grant is performed for illustrative purposes. You should not grant select on SYS objects to regular users.
SQL> CONNECT / AS SYSDBA
Connected.
SQL> GRANT SELECT on user$ TO scott;
Grant succeeded.

SQL> CONNECT scott/tiger
Connected.
SQL> SELECT count(*) FROM sys.user$;

  COUNT(*)
----------
       151
As you can see, if a user is granted object privilege on SYS objects, they can access it.

- What is the difference between SELECT ANY DICTIONARY privilege and SELECT_CATALOG_ROLE role?
     - SELECT ANY DICTIONARY provides access to more SYS tables (for example, select on user$ table, but not SELECT_CATALOG_ROLE.
     - Also, if you are granted the role but not the privilege, you may not do any SELECT on the sys tables/views you are allowed to access in a PL/SQL procedure (you need direct grant to access them.)

How do I know if a given user is logged in as SYSDBA?
- The current user is "SYS"
SQL> SHOW USER;
USER is "SYS"

SELECT SYS_CONTEXT('USERENV', 'SESSION_USER') FROM DUAL;
SYS_CONTEXT('USERENV', 'SESSION_USER')
--------------------------------------------------
SYS

- ISDBA parameter of USERENV system context is TRUE.
SQL> SELECT SYS_CONTEXT('USERENV', 'ISDBA') FROM DUAL;
SYS_CONTEXT('USERENV', 'ISDBA')
--------------------------------------------------
TRUE


What is the difference between SYSDBA and DBA?
- This is another frequently misunderstood concept!
- As mentioned, SYSDBA is the most powerful administrative privilege.
- DBA is a role! DBA role is granted all system privileges.
- If a user is granted DBA role, she can connect to the database only when it is in OPEN status.


What is a database privilege?
- In order to perform any task (e.g. select a table, execute a PL/SQL procedure) on the database, you need to have "access rights" to perform the task. These access rights are called privileges (e.g. SELECT privilege on the table, EXECUTE privilege on the PL/SQL procedure).
- In formal terms, privileges "authorizes" users to perform certain tasks.

What kind of privileges do we have in an Oracle database?
The following diagram shows the kind of privileges that an Oracle database have.
Figure: Oracle Database Privileges

What are administrative privileges?


Figure: Administrative Privileges

- Administrative privileges allow to perform regular database operations (such as selecting from a table, creating indexes) as well as database maintenance operations such as patching, migration, backing up, tuning, etc. 
- Prior to 12c, there were only two administrative privileges: SYSDBA, and SYSOPER. These are very powerful privileges and should be granted to uses with care. 
- SYSDBA privilege is equivalent to super user in Linux operating system.
- 12c introduced the following administrative privileges to provide better separation of duty and reduce the use of SYSDBA privilege.
  - SYSBACKUP (for backup and recovery operations)
  - SYSDG (for data guard operations)
  - SYSKM (for key management for TDE)
  - SYSRAC (for managing Real Application Clusters)
Figure: All Administrative Privileges are subset of SYSDBA
You can query the V$PWFILE_USERS to see the administrative users:
SQL> select username, sysdba, sysoper, sysasm, sysbackup, sysdg, syskm from V$PWFILE_USERS;

USERNAME             SYSDBA SYSOPER SYSBA SYSDG SYSKM
-------------------- ------ ------- ----- ----- -----
SYS                  TRUE   TRUE    FALSE FALSE FALSE
DBA0                 FALSE  TRUE    FALSE FALSE FALSE
DBA1                 FALSE  FALSE   FALSE FALSE FALSE
DBA1                 FALSE  FALSE   TRUE  FALSE FALSE
DBA2                 FALSE  FALSE   FALSE TRUE  FALSE
DBA3                 FALSE  FALSE   FALSE FALSE TRUE

What are system privileges and what are object privileges?
Figure: System and Object Privileges

- System privileges are applicable to the complete system and not specific to a given database object. For example, SELECT ANY TABLE system privilege allows to select from any table in the database.
- System privileges are very powerful and thus should be granted with care.
- Object privileges on the other hand grant access rights to specific database objects. For example, SELECT on scott.emp table.
- Object privileges support least privilege security principle but they are difficult to maintain.

How administrative privileges different from other privileges?
- Administrative privileges can be used even when database is not open.
- If a user is granted multiple administrative privileges (for example, SYS user is granted SYSDBA and SYSOPER administrative privileges), the user can login as only one administrative privilege user (for example as SYSDBA or as SYSOPER).
  - CONNECT SYS/<password> AS SYSDBA
  - CONNECT SYS/<password> AS SYSOPER
- When a user connects as SYSDBA (e.g. CONNECT scott/tiger AS SYSDBA), the current user is not the login user (i.e. scott), but is SYS user.
- When a user connects as SYSOPER (e.g. CONNECT scott/tiger AS SYSOPER), the current user is also not the login user (i.e. scott), but is PUBLIC user. 

How do I look up who is granted which privileges?
- SYS.V$PWFILE_USERS for administrative privileges
- SYS.DBA_SYS_PRIVS for system privileges
- SYS.DBA_TAB_PRIVS for object privileges

Regular Expression Based Redaction Policy

We can use the regular expression based redaction technique to mask a column data based on a pattern match when the function_type parameter is set to DBMS_REDACT.REGEXP. After setting the function_type parameter, we have to make use of the below parameters to build our regular expression based pattern matching.
 
·         REGEXP_PATTERN         parameter is used for defining the search pattern for matching the data.
 
·         REGEXP_REPLACE_STRING parameter is used for replacing the strings that are matched by the REGEXP_PATTERN parameter.
 
·         REGEXP_POSITION parameter specifies the starting position for the string search and replacement. This parameter accepts a positive integer indicating the column_name parameter’s character position. The default is 1 or RE_BEGINNING format, meaning that the search starts from the first character of the column data.
 
·         REGEXP_OCCURRENCE parameter defines at which occurrence the search and replace must occur. This parameter accepts all positive numbers to indicate the replace option.
 
è If we specify 0 or RE_ALL as its value, then all the occurrences of the match get replaced.
 
è If we specify 1 or RE_FIRST as its value, then the first occurrence of the match gets replaced.
 
è If we specify any positive integer, say n, then the nth occurrence of the match gets replaced.
 
·         REGEXP_MATCH_PARAMETER parameter allows us to change the default matching behavior. For e.g., to make the matching case insensitive, we must use the character i or the format RE_MATCH_CASE_INSENSITIVE.
 
We can use the below formats in place of the values for the REGEXP_PATTERN and REGEXP_REPLACE_STRING parameters in the DBMS_REDACT.ADD_POLICY procedure.
 
REGEXP_PATTERN & REGXP_REPLACE_STRING Default Formats
Format
Description
DBMS_REDACT.
RE_PATTERN_ANY_DIGIT
Searches for any digit. Below are the supported REGEXP_REPLACE_STRING default formats.
 
FORMAT
DESCRIPTION
DBMS_REDACT.
RE_REDACT_WITH_SINGLE_X
Replaces the data with a single X character for each actual data character.
DBMS_REDACT.
RE_REDACT_WITH_SINGLE_1
Replaces the data with a single 1 digit for each actual character.
DBMS_REDACT.
RE_PATTERN_CC_L6_T4
Searches for the digits in the credit card number having 6 leading and 4 trailing digits. Below are the supported REGEXP_REPLACE_STRING default formats.
 
FORMAT
DESCRIPTION
DBMS_REDACT.
RE_REDACT_CC_MIDDLE_DIGITS
Replaces the middle digits in a credit card number.
DBMS_REDACT.
RE_PATTERN_US_PHONE
Searches for any US telephone number format. Below are the supported REGEXP_REPLACE_STRING default formats.
 
FORMAT
DESCRIPTION
DBMS_REDACT.
RE_REDACT_PHONE_L7
Replaces the last 7 digits of a US telephone number.
DBMS_REDACT.
RE_PATTERN_EMAIL_ADDRESS
Searches for any email address. Below are the supported REGEXP_REPLACE_STRING default formats.
 
FORMAT
DESCRIPTION
DBMS_REDACT.
RE_REDACT_EMAIL_NAME
Replaces the email name.
DBMS_REDACT.
RE_REDACT_EMAIL_DOMAIN
Replaces the email domain name.
DBMS_REDACT.
RE_PATTERN_IP_ADDRESS
Searches for any IP address. Below are the supported REGEXP_REPLACE_STRING default formats.
 
FORMAT
DESCRIPTION
DBMS_REDACT.
RE_REDACT_IP_L3
Replaces the last 3 digits of the IP address.
 
In the below code listing, we have created a policy for redacting the email name from a list of email addresses using the default parameter values as shown below,
 
·         REGEXP_PATTERN parameter value as DBMS_REDACT.RE_PATTERN_EMAIL_ADDRESS.
 
·         REGEXP_REPLACE_STRING parameter value as DBMS_REDACT.RE_REDACT_EMAIL_NAME.
 
·         REGEXP_POSITION parameter value as DBMS_REDACT.RE_BEGINNING.
 
·         REGXP_OCCURRENCE parameter value as DBMS_REDACT.RE_FIRST.
 
·         REGEXP_MATCH_PARAMETER parameter value as DBMS_REDACT.RE_MATCH_CASE_INSENSITIVE.
 
BEGIN
 DBMS_REDACT.ADD_POLICY(
   object_schema          => 'C##',
   object_name            => 'CUSTOMER',
   column_name            => 'CUSTOMER_EMAIL',
   policy_name            => 'REDACT_CUSTOMER_EMAIL',
   function_type          => DBMS_REDACT.REGEXP,
   expression             => '1=1',
   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_FIRST,
   regexp_match_parameter => DBMS_REDACT.RE_MATCH_CASE_INSENSITIVE);
END;
/
 
When we query the CUSTOMER table, we can see the name of the email address column being redacted to the default character x as shown below,
 
SELECT unredacted, customer_email FROM customer;
Script Result:
UNREDACTED
CUSTOMER_EMAIL
 
In the case of customized regular expression redaction policy, we can enter our own regular expression match pattern, replace string, position value, occurrence value, and the match parameter values instead of the default parameter formats. In our below example, the account number column of 11 characters each, for a set of customers has been redacted by replacing the first 7 characters with the user defined string x. Here, the parameter values used are,
 
·         REGEXP_PATTERN parameter value as '(\d\d\d\d)(\d\d\d)(\d\d\d\d)'.
 
·         REGEXP_REPLACE_STRING parameter value as 'XXXXXX\3'.
 
·         REGEXP_POSITION parameter value as 1.
 
·         REGEXP_OCCURRENCE parameter value as 0.
 
·         REGEXP_MATCH_PARAMETER parameter value as 'i'.
 
BEGIN
 DBMS_REDACT.ADD_POLICY(
   object_schema          => 'C##',
   object_name            => 'CUSTOMER',
   column_name            => 'CUST_ACC_NUM',
   policy_name            => 'REDACT_CUST_ACC_NUM',
   function_type          => DBMS_REDACT.REGEXP,
   expression             => '1=1',
   regexp_pattern         => '(\d\d\d\d)(\d\d\d)(\d\d\d\d)',
   regexp_replace_string  => 'XXXXXX\3',
   regexp_position        => 1,
   regexp_occurrence      => 0,
   regexp_match_parameter => 'i');
END;
/
 
When we query the account number field from the CUSTOMER table, we can see that the column has been redacted with 4 x characters in the place of first 7 characters.
 
SELECT unredacted, cust_acc_num FROM customer;
Script Result:
UNREDACTED
CUST_ACC_NUM
98237959239
XXXX9239
87395873094
XXXX3094
53069840568
XXXX0568
83859034854
XXXX4854

Data redaction

Setup

We need to make sure the test user has access to the DBMS_REDACT package.
CONN sys@pdb1 AS SYSDBA
GRANT EXECUTE ON sys.dbms_redact TO test;
The example code in this article requires the following test table.
CONN test/test@pdb1

DROP TABLE payment_details PURGE;

CREATE TABLE payment_details (
  id          NUMBER       NOT NULL,
  customer_id NUMBER       NOT NULL,
  card_no     NUMBER       NOT NULL,
  card_string VARCHAR2(19) NOT NULL,
  expiry_date DATE         NOT NULL,
  sec_code    NUMBER       NOT NULL,
  valid_date  DATE,
  CONSTRAINT payment_details_pk PRIMARY KEY (id)
);

INSERT INTO payment_details VALUES (1, 4000, 1234123412341234, '1234-1234-1234-1234', TRUNC(ADD_MONTHS(SYSDATE,12)), 123, NULL);
INSERT INTO payment_details VALUES (2, 4001, 2345234523452345, '2345-2345-2345-2345', TRUNC(ADD_MONTHS(SYSDATE,12)), 234, NULL);
INSERT INTO payment_details VALUES (3, 4002, 3456345634563456, '3456-3456-3456-3456', TRUNC(ADD_MONTHS(SYSDATE,12)), 345, NULL);
INSERT INTO payment_details VALUES (4, 4003, 4567456745674567, '4567-4567-4567-4567', TRUNC(ADD_MONTHS(SYSDATE,12)), 456, NULL);
INSERT INTO payment_details VALUES (5, 4004, 5678567856785678, '5678-5678-5678-5678', TRUNC(ADD_MONTHS(SYSDATE,12)), 567, NULL);
COMMIT;

ALTER SESSION SET nls_date_format='DD-MON-YYYY';

COLUMN card_no FORMAT 9999999999999999

SELECT *
FROM   payment_details
ORDER BY id;

        ID CUSTOMER_ID           CARD_NO CARD_STRING         EXPIRY_DATE   SEC_CODE VALID_DATE
---------- ----------- ----------------- ------------------- ----------- ---------- -----------
         1        4000  1234123412341234 1234-1234-1234-1234 28-OCT-2015        123
         2        4001  2345234523452345 2345-2345-2345-2345 28-OCT-2015        234
         3        4002  3456345634563456 3456-3456-3456-3456 28-OCT-2015        345
         4        4003  4567456745674567 4567-4567-4567-4567 28-OCT-2015        456
         5        4004  5678567856785678 5678-5678-5678-5678 28-OCT-2015        567

5 rows selected.

SQL>

Add a new Policy

Creating a new redaction policy is done using the ADD_POLICY procedure in the DBMS_REDACT package. A policy is made up of several distinct sections.
  • Identify the object : The OBJECT_SCHEMA, OBJECT_NAME and COLUMN_NAME parameters identify the column to be redacted.
  • Give it a name : The POLICY_NAME parameter assigns a name to the policy.
  • What should happen? : The FUNCTION_TYPE parameter determines the type of redaction that should take place. The allowable values are listed here. Depending on the type of redaction selected, you may be required to specify the FUNCTION_PARAMETERS or various REGEXP_* parameters.
  • When should it happen? : The EXPRESSION parameter determines when the redaction should take place. For example, an expression of "1=1" means the redaction will always take place. Alternatively, situational expressions can be defined using the SYS_CONTEXT function.
The following example is about as simple as it gets. A full redaction policy is placed on the CARD_NO column with an expression of "1=1".
CONN test/test@pdb1

BEGIN
  DBMS_REDACT.add_policy(
    object_schema => 'test',
    object_name   => 'payment_details',
    column_name   => 'card_no',
    policy_name   => 'redact_card_info',
    function_type => DBMS_REDACT.full,
    expression    => '1=1'
  );
END;
/

ALTER SESSION SET nls_date_format='DD-MON-YYYY';
COLUMN card_no FORMAT 9999999999999999

SELECT *
FROM   payment_details
ORDER BY id;

        ID CUSTOMER_ID           CARD_NO CARD_STRING         EXPIRY_DATE   SEC_CODE VALID_DATE
---------- ----------- ----------------- ------------------- ----------- ---------- -----------
         1        4000                 0 1234-1234-1234-1234 28-OCT-2015        123
         2        4001                 0 2345-2345-2345-2345 28-OCT-2015        234
         3        4002                 0 3456-3456-3456-3456 28-OCT-2015        345
         4        4003                 0 4567-4567-4567-4567 28-OCT-2015        456
         5        4004                 0 5678-5678-5678-5678 28-OCT-2015        567

5 rows selected.

SQL>
We can see the CARD_NO column is now redacted to the number "0". The value displayed by full redaction is based on the data type defaults for the DBMS_REDACT.FULL function type. You can see the default values by querying the REDACTION_VALUES_FOR_TYPE_FULL view, shown by the following example that uses the redaction_columns.sql script.
CONN sys@pdb1 AS SYSDBA

@redaction_value_defaults.sql

NUMBER_VALUE BINARY_FLOAT_VALUE BINARY_DOUBLE_VALUE CHAR_VALUE VARCHAR_VA NCHAR_VALU NVARCHAR_V DATE_VALUE           TIMESTAMP_VALUE             TIMESTAMP_WITH_TIME_ZONE_VALUE   BLOB_VALUE           CLOB_VALUE NCLOB_VALU
------------ ------------------ ------------------- ---------- ---------- ---------- ---------- -------------------- --------------------------- -------------------------------- -------------------- ---------- ----------
           0                  0                   0                                             01-JAN-2001 00:00:00 01-JAN-2001 01:00:00.000000 01-JAN-01 01.00.00.000000 +00:00 5B72656461637465645D [redacted] [redacted]

1 row selected.

SQL>
These default values can be altered using the DBMS_REDACT.UPDATE_FULL_REDACTION_VALUES procedure, but you will need to start the instance for the updates to be visible.
Information about existing policies is displayed using the REDACTION_COLUMNS and REDACTION_POLICIES views, used in the redaction_policies.sql and redaction_columns.sql scripts below.
CONN sys@pdb1 AS SYSDBA

@redaction_policies

OBJECT_OWNER         OBJECT_NAME                    POLICY_NAME                    EXPRESSION                     ENABLE  POLICY_DESCRIPTION
-------------------- ------------------------------ ------------------------------ ------------------------------ ------- --------------------
TEST                 PAYMENT_DETAILS                redact_card_info               1=1                            YES

1 row selected.

SQL>


@redaction_columns

OBJECT_OWNER         OBJECT_NAME                    COLUMN_NAME                    FUNCTION_TYPE               FUNCTION_PARAMETERS            REGEXP_PATTERN                 REGEXP_REPLACE_STRING          REGEXP_POSITION REGEXP_OCCURRENCE REGEXP_MAT COLUMN_DESCRIPTION
-------------------- ------------------------------ ------------------------------ --------------------------- ------------------------------ ------------------------------ ------------------------------ --------------- ----------------- ---------- --------------------
TEST                 PAYMENT_DETAILS                CARD_NO                        FULL REDACTION                                                                                                                         0                 0

1 row selected.

SQL>
Other variations on redaction policies are described in the following section. These are equally applicable during redaction policy creation.

Alter an Existing Policy

The ALTER_POLICY procedure allows you to make changes to an existing policy. The type of change being made is controlled using the ACTION parameter. Depending on the action required, the relevant parameters must be specified.
The following example changes the previously created redaction policy so that it uses partial redaction. Notice the FUNCTION_PARAMETERS are now specified to give instructions how the partial redaction should take place. For a numeric data type we specify a comma separated list of three elements (value to redact to, start point, end point), so in this case we want the first 12 characters of the number to always display as "111111111111".
CONN test/test@pdb1

BEGIN
  DBMS_REDACT.alter_policy (
    object_schema       => 'test',
    object_name         => 'payment_details',
    policy_name         => 'redact_card_info',
    action              => DBMS_REDACT.modify_column,
    column_name         => 'card_no',
    function_type       => DBMS_REDACT.partial,
    function_parameters => '1,1,12'
  );
END;
/
   
ALTER SESSION SET nls_date_format='DD-MON-YYYY';
COLUMN card_no FORMAT 9999999999999999

SELECT *
FROM   payment_details
ORDER BY id;

        ID CUSTOMER_ID           CARD_NO CARD_STRING         EXPIRY_DATE   SEC_CODE VALID_DATE
---------- ----------- ----------------- ------------------- ----------- ---------- -----------
         1        4000  1111111111111234 1234-1234-1234-1234 28-OCT-2015        123
         2        4001  1111111111112345 2345-2345-2345-2345 28-OCT-2015        234
         3        4002  1111111111113456 3456-3456-3456-3456 28-OCT-2015        345
         4        4003  1111111111114567 4567-4567-4567-4567 28-OCT-2015        456
         5        4004  1111111111115678 5678-5678-5678-5678 28-OCT-2015        567

5 rows selected.

SQL>
We can add another column to the redaction policy to protect the string representation of the card number.
BEGIN
  DBMS_REDACT.alter_policy (
    object_schema       => 'test',
    object_name         => 'payment_details',
    policy_name         => 'redact_card_info',
    action              => DBMS_REDACT.add_column,
    column_name         => 'card_string',
    function_type       => DBMS_REDACT.partial,
    function_parameters => 'VVVVFVVVVFVVVVFVVVV,VVVV-VVVV-VVVV-VVVV,#,1,12'
  );
END;
/

ALTER SESSION SET nls_date_format='DD-MON-YYYY';
COLUMN card_no FORMAT 9999999999999999

SELECT *
FROM   payment_details
ORDER BY id;

        ID CUSTOMER_ID           CARD_NO CARD_STRING         EXPIRY_DATE   SEC_CODE VALID_DATE
---------- ----------- ----------------- ------------------- ----------- ---------- -----------
         1        4000  1111111111111234 ####-####-####-1234 28-OCT-2015        123
         2        4001  1111111111112345 ####-####-####-2345 28-OCT-2015        234
         3        4002  1111111111113456 ####-####-####-3456 28-OCT-2015        345
         4        4003  1111111111114567 ####-####-####-4567 28-OCT-2015        456
         5        4004  1111111111115678 ####-####-####-5678 28-OCT-2015        567

5 rows selected.

SQL>
The following example redacts the expiry date using partial redaction, converting the day and month values to 1st of January.
BEGIN
  DBMS_REDACT.alter_policy (
    object_schema       => 'test',
    object_name         => 'payment_details',
    policy_name         => 'redact_card_info',
    action              => DBMS_REDACT.add_column,
    column_name         => 'expiry_Date',
    function_type       => DBMS_REDACT.partial,
    function_parameters => 'm1d1Y'
  );
END;
/

ALTER SESSION SET nls_date_format='DD-MON-YYYY';
COLUMN card_no FORMAT 9999999999999999

SELECT *
FROM   payment_details
ORDER BY id;

        ID CUSTOMER_ID           CARD_NO CARD_STRING         EXPIRY_DATE   SEC_CODE VALID_DATE
---------- ----------- ----------------- ------------------- ----------- ---------- -----------
         1        4000  1111111111111234 ####-####-####-1234 01-JAN-2015        123
         2        4001  1111111111112345 ####-####-####-2345 01-JAN-2015        234
         3        4002  1111111111113456 ####-####-####-3456 01-JAN-2015        345
         4        4003  1111111111114567 ####-####-####-4567 01-JAN-2015        456
         5        4004  1111111111115678 ####-####-####-5678 01-JAN-2015        567

5 rows selected.

SQL>
We can also amend the policy so it does not affect the schema owner. The following example uses the SYS_CONTEXT function in the EXPRESSION parameter to determine the current user, making the application of the redaction policy conditional.
CONN test/test@pdb1

BEGIN
  DBMS_REDACT.alter_policy (
    object_schema       => 'test',
    object_name         => 'payment_details',
    policy_name         => 'redact_card_info',
    action              => DBMS_REDACT.modify_expression,
    column_name         => 'card_no',
    expression          => 'SYS_CONTEXT(''USERENV'',''SESSION_USER'') != ''TEST'''
  );
END;
/

-- Test on current user.
ALTER SESSION SET nls_date_format='DD-MON-YYYY';
COLUMN card_no FORMAT 9999999999999999

SELECT *
FROM payment_details
ORDER BY id;

        ID CUSTOMER_ID           CARD_NO CARD_STRING         EXPIRY_DATE   SEC_CODE VALID_DATE
---------- ----------- ----------------- ------------------- ----------- ---------- -----------
         1        4000  1234123412341234 1234-1234-1234-1234 28-OCT-2015        123
         2        4001  2345234523452345 2345-2345-2345-2345 28-OCT-2015        234
         3        4002  3456345634563456 3456-3456-3456-3456 28-OCT-2015        345
         4        4003  4567456745674567 4567-4567-4567-4567 28-OCT-2015        456
         5        4004  5678567856785678 5678-5678-5678-5678 28-OCT-2015        567

5 rows selected.

SQL>


-- Connect to another user and test.
GRANT SELECT ON test.payment_details TO test2;

CONN test2/test2@pdb1

ALTER SESSION SET nls_date_format='DD-MON-YYYY';
COLUMN card_no FORMAT 9999999999999999

SELECT *
FROM   test.payment_details
ORDER BY id;

        ID CUSTOMER_ID           CARD_NO CARD_STRING         EXPIRY_DATE   SEC_CODE VALID_DATE
---------- ----------- ----------------- ------------------- ----------- ---------- -----------
         1        4000  1111111111111234 ####-####-####-1234 01-JAN-2015        123
         2        4001  1111111111112345 ####-####-####-2345 01-JAN-2015        234
         3        4002  1111111111113456 ####-####-####-3456 01-JAN-2015        345
         4        4003  1111111111114567 ####-####-####-4567 01-JAN-2015        456
         5        4004  1111111111115678 ####-####-####-5678 01-JAN-2015        567

5 rows selected.

SQL>
As expected, the redaction policy no longer applies to the TEST user.
Details of the FUNCTION_PARAMETERS formats is available here. There are also predefined Partial Fixed Character Redaction Formats listed here.

Drop an Existing Policy

The DROP_POLICY procedure is used to remove an existing redaction policy. The following example drops the redaction policy and queries the data, showing the redaction is no longer taking place.
CONN test/test@pdb1

BEGIN
  DBMS_REDACT.drop_policy (
    object_schema => 'test',
    object_name   => 'payment_details',
    policy_name   => 'redact_card_info'
  );
END;
/

ALTER SESSION SET nls_date_format='DD-MON-YYYY';
COLUMN card_no FORMAT 9999999999999999

SELECT *
FROM   payment_details
ORDER BY id;

        ID CUSTOMER_ID           CARD_NO EXPIRY_DATE   SEC_CODE VALID_DATE
---------- ----------- ----------------- ----------- ---------- -----------
         1        4000  1234123412341234 27-OCT-2015        123
         2        4001  2345234523452345 27-OCT-2015        234
         3        4002  3456345634563456 27-OCT-2015        345
         4        4003  4567456745674567 27-OCT-2015        456
         5        4004  5678567856785678 27-OCT-2015        567

5 rows selected.

SQL>

Views

As mentioned previously, you can get information about redaction policies using the following views.
You can see examples scripts using these views here (redaction_policies.sql, redaction_columns.sql, redaction_value_defaults.sql).

Additional Information

  • Redaction will not take place if the user has the EXEMPT REDACTION POLICY system privilege.
  • If you try to CREATE TABLE ... AS SELECT (CTAS) against a redacted table you get the following error message.
    ORA-28081: Insufficient privileges - the command references a redacted object.
  • The DATAPUMP_EXP_FULL_DATABASE role includes the EXEMPT REDACTION POLICY system privilege. As this role is granted to the DBA role, DBAs are excluded from redaction policies.
  • Redaction does not apply to the WHERE clause, so inference of the value is still possible for those with SQL access.
  • Enterprise Manager allows you to create and manage custom partial redaction formats, allowing you to build a library of your favourites. This is not part of the core functionality. This functionality is described here.
  • There have been several high profile bugs related to security holes in redaction. Make sure you are patched.