Data Masking Policies

A data masking policy masks column values in query results for selected users and groups. For example, a support agent can read a customer row, but the result shows a mask in place of the customer's email address. Use table rules or pattern rules to select the columns, and a masking strategy to choose how much of each value stays visible.

Masking applies only to restricted datasources.

Each datasource carries a restrictedAccess flag. That datasource-level switch is what tells Colrows to build the masking index and mask values on every query. On an unrestricted datasource (restrictedAccess: false), masking is skipped entirely, so any policy you define here has no effect until the datasource is restricted. The flag is off unless you set it: the Restricted Access switch starts off when you add a datasource, and the datasource create API treats an omitted restrictedAccess field as false. Turn it on per datasource in Administration → Connections, or send restrictedAccess: true on the datasource API.

Access control vs. masking

Data access policies and data masking policies are separate policy types. Use access policies to control access to data. Use masking policies to mask values in the results:

Data access policyData masking policy
PurposeControl which rows and columns a user can queryMask the values of selected columns
Effect on the resultRestrict rows and columnsKeep the row and column; replace the value with a mask
Execution stageDuring query planningDuring result processing, after the database runs the query

A mask does not grant access, and an access grant does not cancel a mask. Database joins and calculations use the source values before result masking. A partial mask is not a unique key for joins between returned results.

What a policy contains

Masking policies use the same structure as data access policies. The API rejects unknown fields.

FieldDefinition
nameRequired. Unique in your organization.
descriptionOptional free text.
scopeRequired. datasourceId and schema are both required. catalog is reserved and rejected.
subjectsRequired. userIds and groupIds arrays of user IDs and user-group IDs. Use an empty array for the side you don't assign.
rulesRequired. At least one rule. Each rule has ruleType, resourceSelector, and strategy.
policyIdRequired on update. Generated on create.

A policy applies when its datasource matches and the requester matches a listed user or group. Empty user and group arrays do not apply the policy to everyone, and masking policies do not select subjects by attribute. A requester without a matching masking policy has no mask from this policy; access policies still apply.

Selecting columns

Masking rules use the same resource selectors as access rules:

  • TABLE rules name one exact table with tableName, and narrow its columns with includedColumns and excludedColumns. The table and the named columns must exist in the datasource layout.
  • PATTERN rules select tables with tablePattern, and narrow columns with includedColumnPatterns and excludedColumnPatterns. Patterns are Java regular expressions with case-insensitive substring matching; use anchors when the entire name must match.

An absent inclusion set selects all columns of the table. An empty inclusion set selects none. Exclusions are then removed from the selected set.

Masking strategies

Each rule's strategy.type selects how to mask a value, and strategy.visibleCharacterCount sets how many characters stay visible:

strategy.typeEffect on a non-empty stringExample
FULLReplace the value with the fixed token *****. Ignore visibleCharacterCount.alice@example.com → *****
KEEP_PREFIXKeep the first visibleCharacterCount characters. Replace the remaining characters with asterisks.AB123456, count 2 → AB******
KEEP_SUFFIXKeep the last visibleCharacterCount characters. Replace the remaining characters with asterisks.1234567890, count 4 → ******7890
  • For KEEP_PREFIX and KEEP_SUFFIX, visibleCharacterCount must be zero or greater. A count of zero replaces every character with an asterisk.
  • If a non-empty string has no more characters than the count, the result is *****. For example, 1234 with a KEEP_SUFFIX count of 4 returns *****.
  • An empty string remains empty.
  • A non-string value, including a number, date, or null, returns ***** for every strategy.

When rules overlap

When more than one rule masks the same column for a user, Colrows uses the strongest mask:

  • A FULL mask overrides a partial mask.
  • Two partial masks in the same direction use the smaller visible character count.
  • A KEEP_PREFIX and a KEEP_SUFFIX on the same column produce a FULL mask.

How masks reach the output

Colrows maps each masked source column to the query's output columns by its alias or column name. If several output columns carry that label, all of them are masked. If a masked source column cannot be mapped to the output, execution fails, so a computed output that needs source lineage can be refused rather than returned without a mask.

A complete policy: full masking

This policy masks email addresses and phone numbers in analytics.customers for one user and the members of the support group. Replace the placeholders with actual IDs before you use the policy.

{
  "name": "support_contact_masking",
  "scope": { "datasourceId": "<datasource-id>", "schema": "analytics" },
  "subjects": { "userIds": ["<user-id>"], "groupIds": ["<support-group-id>"] },
  "rules": [
    {
      "ruleType": "TABLE",
      "resourceSelector": {
        "tableName": "customers",
        "includedColumns": ["email", "phone"]
      },
      "strategy": { "type": "FULL" }
    }
  ]
}

Masking rules have no effect field. Access rules require ALLOW or DENY.

A complete policy: partial masking

Use separate rules when columns need different strategies. This policy keeps the first two characters of a customer reference and the last four characters of an account number, and fully masks every column whose name ends in _ssn in any table. Columns must contain strings to show a partial mask.

{
  "name": "support_account_masking",
  "scope": { "datasourceId": "<datasource-id>", "schema": "analytics" },
  "subjects": { "userIds": [], "groupIds": ["<support-group-id>"] },
  "rules": [
    {
      "ruleType": "TABLE",
      "resourceSelector": { "tableName": "customers", "includedColumns": ["customer_reference"] },
      "strategy": { "type": "KEEP_PREFIX", "visibleCharacterCount": 2 }
    },
    {
      "ruleType": "TABLE",
      "resourceSelector": { "tableName": "customers", "includedColumns": ["account_number"] },
      "strategy": { "type": "KEEP_SUFFIX", "visibleCharacterCount": 4 }
    },
    {
      "ruleType": "PATTERN",
      "resourceSelector": { "tablePattern": ".*", "includedColumnPatterns": ["_ssn$"] },
      "strategy": { "type": "FULL" }
    }
  ]
}
ColumnSource valueReturned value
customer_referenceAB123456AB******
account_number1234567890 (string)******7890
account_number1234 (string)*****
account_number1234567890 (number)*****

Create a policy in Administration

  1. Open Administration → Data Masking Policies and select Data Masking Policy.
  2. Enter a name of no more than 35 characters. Select the datasource and its schema.
  3. Select the users or groups that need masked results.
  4. Open Rules and enter only the JSON array from the rules field. Do not enter the complete policy document.
  5. Save the policy. Run a query as a selected user and check the returned values.

For example, enter this array in the Rules editor to mask a salary column:

[
  {
    "ruleType": "TABLE",
    "resourceSelector": { "tableName": "salaries", "includedColumns": ["salary"] },
    "strategy": { "type": "FULL" }
  }
]

Manage policies by API

Only administrators can create, update, or delete policies. A server-to-server OAuth client can call these endpoints with the scope shown.

OperationEndpointClient scope
List policies, including deactivated onesGET /api/masking/listaccess-control:read
Get a policyGET /api/masking/get/{policyId}access-control:read
Get a policy at a revisionGET /api/masking/list/{policyId}/{version}access-control:read
Create a policyPOST /api/masking/createaccess-control:read-write
Update a policy (send policyId)PUT /api/masking/updateaccess-control:read-write
Delete (deactivate) a policyDELETE /api/masking/delete/{policyId}access-control:read-write

Each policy carries active, revision, createdBy, createdAt, updatedBy, and updatedAt. Each update or delete increases revision and records the actor; delete sets active to false. Revisions track the current definition only and are not a history archive: the revision lookup returns the policy only when {version} matches its current revision.

Check the policy with the target identity.

Use existing table and column names. Check full masks, partial masks, short strings, and non-string values. Use data access policies when you must restrict rows or prevent access to a column.