# Oracle Dynamic Data Masking

> Compare Oracle data masking options. Native Data Redaction (five function types via DBMS_REDACT, EXEMPT REDACTION POLICY) and Bytebase fleet-wide masking.

Tianzhou | 2026-05-14 | Source: https://www.bytebase.com/blog/oracle-dynamic-data-masking/

---

SSNs, credit cards, emails, addresses. These columns have to stay queryable for support, analytics, and development, but broad cleartext access is not the answer. Data masking is. For GDPR, HIPAA, and PCI workloads, it is also a legal requirement.

Oracle Database ships [Data Redaction](https://docs.oracle.com/en/database/oracle/oracle-database/19/asoag/introduction-to-oracle-data-redaction.html) as part of the Advanced Security option. (Oracle also sells a separate **Data Masking and Subsetting Pack**, static masking for non-prod clones, out of scope here.) **Bytebase Dynamic Data Masking** sits in front of it: one policy model across every Oracle deployment and every other engine in the fleet, with Request, Review, Approve built in. This post compares the two.

## Oracle Data Redaction

Data Redaction is GA on **Oracle Database Enterprise Edition with the Advanced Security option (ASO)**, an extra-cost license. It shipped in 11.2.0.4 and is supported through 12c, 18c, 19c, 21c, and 23ai. Standard Edition and Express Edition do not include it. Oracle Autonomous Database includes ASO; Database@AWS, @Azure, and @Google Cloud follow the same on-prem licensing.

Policies are defined per column with `DBMS_REDACT.ADD_POLICY`. The server rewrites query results for sessions whose policy expression evaluates to TRUE. The data on disk is unchanged.

### Five function types

| Function type                | Constant                  | Behavior                                                                                                                                |
| ---------------------------- | ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| Full                         | `DBMS_REDACT.FULL`        | Replaces the entire value with a fixed default for the datatype. `0` for numbers, single space for `VARCHAR2`, `01-JAN-01` for dates. Defaults configurable via `DBMS_REDACT.UPDATE_FULL_REDACTION_VALUES`. |
| Partial                      | `DBMS_REDACT.PARTIAL`     | Exposes some characters, masks the rest. Built-in shortcuts for common patterns: `REDACT_US_SSN_F5`, `REDACT_CCN16_F12`.                |
| Random                       | `DBMS_REDACT.RANDOM`      | Replaces with a random value of the same datatype. Different value per query.                                                           |
| Regexp                       | `DBMS_REDACT.REGEXP`      | Pattern-based replace. Used for emails, phone numbers, anything not covered by partial.                                                 |
| None                         | `DBMS_REDACT.NONE`        | No redaction. Used to test policy semantics without affecting output.                                                                   |

```sql
-- Full redaction on a salary column for everyone except the payroll role.
BEGIN
  DBMS_REDACT.ADD_POLICY(
    object_schema => 'HR',
    object_name   => 'EMPLOYEES',
    column_name   => 'SALARY',
    policy_name   => 'salary_redact',
    function_type => DBMS_REDACT.FULL,
    expression    => 'SYS_CONTEXT(''USERENV'',''SESSION_USER'') != ''PAYROLL_USER''');
END;
/

-- Partial redaction on SSN: show last 4 digits, mask first 5.
BEGIN
  DBMS_REDACT.ADD_POLICY(
    object_schema       => 'HR',
    object_name         => 'EMPLOYEES',
    column_name         => 'SSN',
    policy_name         => 'ssn_redact',
    function_type       => DBMS_REDACT.PARTIAL,
    function_parameters => DBMS_REDACT.REDACT_US_SSN_F5,
    expression          => '1=1');
END;
/

-- Add a column to an existing policy (12.2+).
BEGIN
  DBMS_REDACT.ALTER_POLICY(
    object_schema => 'HR',
    object_name   => 'EMPLOYEES',
    policy_name   => 'salary_redact',
    action        => DBMS_REDACT.ADD_COLUMN,
    column_name   => 'COMMISSION_PCT',
    function_type => DBMS_REDACT.FULL);
END;
/
```

Pre-12.2, each table was limited to **one redaction policy**. From 12.2 onward, a single policy covers multiple columns via `ALTER_POLICY ... ADD_COLUMN`. The per-table policy object stays singular, but it can carry many columns, each with its own function type.

### Permissions and the policy expression

Two privileges govern the model:

- **`EXECUTE ON DBMS_REDACT`** is required to create or modify policies. Grant it to the security officer.
- **`EXEMPT REDACTION POLICY`** bypasses every redaction policy in the database. The session sees real values. Grant sparingly and audit changes.

The **`expression`** parameter controls whether redaction applies for a session. It evaluates against runtime context, typically `SYS_CONTEXT`:

```sql
-- Mask everyone except the application schema.
expression => 'SYS_CONTEXT(''USERENV'',''SESSION_USER'') != ''APP_USER'''

-- Mask everyone outside an application context set by the app on connect.
expression => 'SYS_CONTEXT(''APP_CTX'',''role'') != ''ANALYST'''
```

`SYS` and users with `SYSDBA` see real data; redaction does not apply to them. Same for any user granted `EXEMPT REDACTION POLICY`. Limit who holds these, and record any grant change to Unified Audit.

### What Data Redaction does not do

- **Stop SYS, SYSDBA, or `EXEMPT REDACTION POLICY` holders.** They see real values. Limit who holds these.
- **Defend against ad-hoc inference.** A user with `SELECT` but no exemption can still probe with predicates. `WHERE salary BETWEEN 99999 AND 100001` returns the redacted `0` but reveals which rows match. Pair with [Database Vault](https://docs.oracle.com/en/database/oracle/oracle-database/19/dvadm/introduction-to-oracle-database-vault.html) and audit.
- **Apply to DML.** `INSERT`, `UPDATE`, and `MERGE` operate on the underlying values. A session with redacted `SELECT` and `UPDATE` can still overwrite the column.
- **Protect Data Pump exports.** `expdp` reads the table directly; redaction does not apply. Anyone with `DATAPUMP_EXP_FULL_DATABASE` exports cleartext. Use the Oracle Data Masking and Subsetting Pack for non-prod clones.
- **Protect RMAN backups.** Backups are physical block copies; redaction is a query-time transform.
- **Apply to internal SQL.** Recursive SQL issued by the Oracle internals (parsing, the optimizer) sees real values.
- **Cover every column type.** Virtual columns (pre-12.2), columns referenced by virtual columns, certain XMLType configurations, columns indexed by Oracle Text, and columns under Editioning Views have restrictions. Check the version-specific [Restrictions on Oracle Data Redaction](https://docs.oracle.com/en/database/oracle/oracle-database/19/asoag/oracle-data-redaction-features-and-capabilities.html#GUID-D1C03D27-1F65-43E1-87B7-37BB1AAA9CC2) page.
- **Compose automatically with Database Vault realms.** The two overlap, and sequencing matters. Database Vault gates *who can access* the table; redaction transforms *what they see*. Configure both, in that order.
- **Filter rows.** Data Redaction operates at the column level. For row-level control, use Virtual Private Database (VPD) or Real Application Security.

## Bytebase Dynamic Data Masking

![_](/content/blog/mysql-dynamic-data-masking/bb-masking-overview.webp)

Native Data Redaction has two documented gaps. `EXEMPT REDACTION POLICY` holders and SYSDBA see cleartext. `SELECT`-only users infer underlying values with ranged predicates. Both follow from the same design: redaction rewrites results inside the engine, but privileges and predicates run upstream of the rewrite. Closing the gaps means governing the **query itself**, not just the result.

[Bytebase Dynamic Data Masking](https://docs.bytebase.com/security/data-masking/overview/) governs the query. Queries route through Bytebase's SQL Editor, and Bytebase masks results before they leave the editor. `SYSDBA` and `EXEMPT REDACTION POLICY` on the backing instance do not bypass the policy. Ad-hoc inference becomes an access decision: granting `Query` rights runs through a built-in workflow, **Request, Review, Approve**, every step audited.

Policies compose from three layers, evaluated in fixed precedence: **Masking Exemption > Global Masking Rule > Column Masking**.

1. **Global Masking Rule.** Workspace-level. Rules evaluate top-down, first match wins. Match conditions span environment, project, database, and data classification. Each match applies a Semantic Type, which selects a masking algorithm: full, partial, MD5, range, or custom.

![_](/content/blog/mysql-dynamic-data-masking/bb-global-masking.webp)

2. **Column Masking.** Project-level override on a specific column when the global rule does not apply.

![_](/content/blog/mysql-dynamic-data-masking/bb-column-masking.webp)

3. **Masking Exemption.** Named users receive time-bound `Query` or `Export` exemptions to specific databases or tables. Service accounts are not eligible. Every grant logged, every access logged.

![_](/content/blog/mysql-dynamic-data-masking/bb-grant-exemption.webp)

Masking propagates. When a column is masked, the policy extends to every view and derived structure that depends on it. Expressions over masked columns stay masked.

![_](/content/blog/mysql-dynamic-data-masking/bb-sql-editor-full-masking.webp)

_Policies can also be codified via [GitOps](https://github.com/bytebase/example-database-security)._

Masking decisions are recorded in the [audit log](/blog/bytebase-audit-logging/#what-bytebase-records). Every SQL execution entry carries per-column masking metadata (masked columns, Semantic Type, matching rule) alongside user, source IP, statement, and row count. Granted exemptions, used exemptions, and policy edits are first-class audit events. The access decision and the proof of enforcement share the same record.

One thing to be clear about: the enforcement boundary. Bytebase masks queries routed through the SQL Editor. Traffic that hits Oracle directly bypasses it, and is covered there by native Data Redaction. The pattern is symmetric: native redaction at the database, Bytebase on the human query path where approval and audit matter. One policy applies across Oracle Database EE, SE, Autonomous Database, Database@AWS / @Azure / @Google Cloud, and AWS RDS for Oracle.

## Comparison

|                  | Oracle Data Redaction                                                | Bytebase Dynamic Data Masking                       |
| ---------------- | -------------------------------------------------------------------- | --------------------------------------------------- |
| Compatibility    | Oracle EE 11.2.0.4+ with ASO; Autonomous Database; Database@AWS/Azure/Google | All Oracle distributions including SE ⭐️           |
| Mechanism        | `DBMS_REDACT.ADD_POLICY` per table ⭐️                                | Policy in Bytebase, applied at SQL Editor          |
| Enforced at      | Database, every read path ⭐️                                         | SQL Editor                                          |
| Function types   | Five built-in                                                        | Full, partial, MD5, range, custom                   |
| Policy mgmt      | PL/SQL on each database                                              | Centralized UI, grants, audit log ⭐️               |
| Permission scope | Table / column (one policy per table pre-12.2; multi-column from 12.2) | Project, database, table, column                  |
| Workflow         | PL/SQL only                                                          | Request. Review. Approve. ⭐️                       |
| Row-level filter | No (pair with VPD or RAS)                                            | No (pair with access policy)                        |
| License          | Advanced Security option (extra cost)                                | Bytebase Enterprise                                 |

## Picking one

- **Single Oracle deployment with ASO already licensed, and masking must enforce regardless of client.** Use Data Redaction. It is in-engine, on every read path, and the only option that masks JDBC, OCI, and SQL\*Plus sessions equally for non-exempt users. Pair it with Database Vault to keep DBAs out of the redacted columns and Unified Audit to record privilege grants.
- **Standard Edition, or no ASO budget.** Native Data Redaction is not available. Use Bytebase on the human query path, plus a separate process for direct connections (limit `SELECT` grants on sensitive tables, audit those grants).
- **Mixed fleet, Oracle alongside Postgres, MySQL, SQL Server, Snowflake.** Use Bytebase. One policy model, every engine, audited grants for every unmask, recorded in the same place as your access logs.
- **Both.** Data Redaction at the database for direct connections, JDBC, and exports for users without `EXEMPT`. Bytebase governs the human query path through the SQL Editor with approval and audit. They compose.

---

Try Bytebase Dynamic Data Masking with [this tutorial](https://docs.bytebase.com/tutorials/data-masking/).