Data masking with APEX


A common customer requirement is to mask sensitive or personally identifiable data from APEX reports.

Oracle has a ‘Data Masking and Subsetting’ product that performs this task.

However, for smaller APEX projects, the full blown data masking product might be overkill as it needs familiarity with the product and configuration. This may be time consuming and expensive.

However, we are able to use the PL/SQL package DBMS_REDACT to achieve the same result.

Test Environment

  • APEX 22.1.5 on-premise (September 2022)
  • APEX 22.1.4 (AlwaysFree) (September 2022)

This functionality is available on on-premise APEX environments and the AlwaysFree Oracle Cloud environment.

The data masking functionality is not available on as it requires access to the DBMS_REDACT package which is owned by ‘SYS’.

User accounts

Create two APEX users called MANAGER and INTERN.

Sample data

Create an employee table with a couple of sensitive fields for the test scenario.

create table gdpr_emp
(id number generated by default on null as identity,
 first_name varchar(100),
 last_name varchar(100),
 ni_number varchar(100),
 salary number,
 email_address varchar(100),
 date_of_birth date,
 country varchar(100),
 credit_card varchar(100)

Insert a sample record.

insert into gdpr_emp
 to_date('01-JAN-1970', 'DD-MON-YYYY'),

APEX application

  • Create a APEX application named ‘GDPR_DEMO’.

  • Create a page named ‘Employees’ with an interactive report based on the ‘GDPR_EMP’ table.

Run the GDPR_DEMO application and login as ‘MANAGER’ and ‘INTERN’. All fields should be visible on the ‘Employees’ page.


Create the redaction policy

The APEX schema needs privileges to access the DBMS_REDACT package. Login as SYS and grant the privileges.

grant execute on sys.dbms_redact to <APEXDEMO>;

Create the redaction policy. The expression parameter defines which user accounts do not have access to the actual values of the redacted columns.

According to this redaction policy, if the user account is ‘INTERN’, the CREDIT_CARD column value should be redacted. This means the ‘INTERN’ user will see zeroes in place of the actual values in this column.

    object_schema => 'APEXDEMO',
    object_name => 'GDPR_EMP',
    policy_name => 'GDPR Demo',
    expression => 'v(''APP_USER'') = ''INTERN''',
    column_name => 'CREDIT_CARD',
    function_type => dbms_redact.full

Add the EMAIL_ADDRESS and SALARY columns by modifying the existing redaction policy.

    object_schema => 'APEXDEMO',
    object_name => 'GDPR_EMP',
    policy_name => 'GDPR Demo',
    action => dbms_redact.add_column,
    column_name => 'EMAIL_ADDRESS',
    function_type => dbms_redact.full
    object_schema => 'APEXDEMO',
    object_name => 'GDPR_EMP',
    policy_name => 'GDPR Demo',
    action => dbms_redact.add_column,
    column_name => 'SALARY',
    function_type => dbms_redact.full

You can query the current redaction configuration by querying the REDACTION_COLUMNS, REDACTION_POLICIES and REDACTION_VALUES_FOR_TYPE_FULL views (as ‘SYS’).


Now login to the GDPR_DEMO application as ‘MANAGER’. You will see the full, unredacted data as normal.

Now login as ‘INTERN’. You will note that the email address and credit fields are redacted (spaces are displayed while the ‘Salary’ field is displayed as 0 (zero).



This is a simple example of data redaction.

Other possible solutions would be to use APEX authorisation schemes to completely hide the sensitive columns from the ‘INTERN’ user.

This has the advantage of preventing user confusion where the user can see a sensitive field but not the actual value which may be mistaken as a bug.

There could also be finer levels of granularity:

  • SuperUser
  • HR Manager
  • Employee
  • Contractor
  • Intern


To remove the redaction policy

  dbms_redact.drop_policy (
    object_schema => 'APEXDEMO',
    object_name => 'GDPR_EMP',
    policy_name => 'GDPR Demo');