Dynamic Masking
Satori provides you with the ability to perform real-time masking of sensitive data. Configuring dynamic masking involves several key components:
Data Classification – Satori automatically classifies data in the data inventory. You can also configure custom classifiers or manually classify data. To enable dynamic masking, ensure the relevant fields are correctly classified in the data inventory.
For more information on data classification, refer to the Data Inventory chapter.
Security Policies – Security policies contain dynamic masking rules, specifying which masking profile to apply based on user roles and conditions. For example, apply the Customer Success masking profile when the user is a member of the Customer Success group and the customer's country is United States.
For more information on security policies, refer to the Security Policies chapter.
Masking Profiles – Masking profiles determine how data is transformed when a masking policy is enforced. For instance, when querying email addresses, a masking profile can obscure the username portion while keeping the domain visible.
For more information on masking profiles, continue reading below.
Masking Profiles
Satori uses masking profiles to simplify the configuration of dynamic masking. Masking profiles define the set of transformations to apply to each data type. Multiple rules can reuse the same profile.

Data Transformations
Satori supports two types of data transformations:
- Generic Data Transformations - These transformations can be applied to any data type. For example, by replacing data fields with predefined strings, hashing the data or removing it completely from the result set.
- Specific Data Transformations - Tailor-made transformations for common data types that provide a better user experience for users when masking data. For example, anonymizing an email address by replacing the address prefix with * or retaining only the year for a date of birth field.
To see the full list of transformations, refer to the link here Transformations List.

Masking Profile Templates
Satori provides three pre-configured profile templates for common masking use-cases.
- Permissive Masking Profile - This masking profile is suited for roles that require some access to PII.
- Restrictive Masking Profile - This masking profile is suited for any role that does not require access to PII.
- Analytics Masking Profile - This masking profile is suited for analytics teams that need to retain statistical data characteristics while protecting PII.
Masking Profile Attributes
Masking profiles are comprised of the following attributes:
- Name - a unique name of the profile.
- Description - a short description of the profile.
- Masking Conditions - a list of masking conditions.
Masking conditions define which transformation to apply for every classifier. Only one condition can be set for each classifier, such as EMAIL, PII or a custom classifier.
Creating a Dynamic Masking Rule
Masking profiles are used by security policies when defining dynamic masking rules. To create a dynamic masking rule, create a masking policy or edit an existing one. There are two modes for configuring dynamic masking rules, simple and advanced.
Simple Dynamic Masking Rule
When using the simple masking rule mode, only one dynamic masking rule can be defined in the security policy, which will apply the same masking profile for all users. To use the simple dynamic masking mode, perform the following steps:
- Open your security policy.
- Select the Dynamic Masking tab.
- Select the masking profile to enforce.
- Click Save.

Advanced Dynamic Masking Rule
In the advanced dynamic masking rule mode, multiple masking rules can be defined. For each rule, a different masking profile can be applied, and masking rules can match user queries based on various attributes. To use the advanced dynamic masking rule mode, perform the following steps:
- Open your security policy.
- Select the Dynamic Masking tab.
- Click on the Advanced button to configure the first rule.
- Select to which users the rule should be match.
- Select the masking profile to apply.
- Click on the plus or minus buttons to add or remove rules.
- Click Save.
Note: You can toggle on or off individual masking rules.

Select Users to Match to a Masking Rule
You can use several options to select which users match a masking rule.
The user is / is not
The dynamic masking rule should match a specific identity or all other users except a specific identity. There are multiple options for this identity:
- User - a user that is defined in the User Management page. For example:
john.smith@acme.com. - Datastore Username - a user that is defined locally in the data store. For example:
JSMITHon a Snowflake account.
The user is a member / is not a member of
The dynamic masking rule matches if the user is or is not a member of a group. Several types of groups are supported:
- Group - refers to a Satori directory group. This option is only available for data stores that use the proxy-based integration.
- IdP Group - refers to a group that is managed by the organization’s identity provider and that is synchronized to Satori using the SCIM integration or SAML attributes. This option is only available for data stores that use the proxy-based integration.
- Databricks Group - refers to a group in a Databricks account
- Snowflake Role - refers to an account role in a Snowflake account. This option is only available for the Snowflake Native integration.
Custom Expression
Use this option to match users using a custom expression in a CEL (Common Expression Language) format. This enables you to create Attribute-Based Access Control (ABAC) masking rules, reducing the number of rules you need to manage.
You can either synchronize user attributes from your identity provider using the SCIM integration or manage them directly on Satori in the User Management view.
Satori provides the following built-in functions to create CEL expressions:
userMemberOfGroup - checks if a user is a member of the specified group. For example: userMemberOfGroup('groupA').
userHasAttr - checks if a user has an attribute with the specified name. For example: userHasAttr('attribute_name').
userAttr - returns the value of the user attribute with the specified name, or null if no such attribute exists. For example: userAttr('country') == 'value_of_attribute_name'.
You can also create nested logic rules using parenthesis, boolean OR (||) and boolean AND (&&). For example: userAttr('country') == 'US' && userAttr('team') = 'Customer Success'.

Conditional Masking
Conditional masking (sometimes referred to as cell-level security) enables you to apply masking profiles to specific rows of the result set.
To define a conditional masking rule add the SQL expression to determine whether or not to filter a specific cell in the Where section of the rule.
NOTE: Conditional masking is only available in the Databricks and Snowflake Native integrations.
Conditional Masking Examples
To apply a masking profile to rows where the region field is Europe, use the following SQL expression: region = 'Europe'.
To apply a masking profile to rows where the created_at field is before a certain date, use the following SQL expression: created_at < '2025-01-01'.
To apply a masking profile to rows where the team field does not equal a user’s team, assuming another table in the database contains that information, use the following SQL expression: team != (SELECT team FROM teams WHERE user = CURRENT_USER()).
To apply a masking profile in Snowflake to objects where the INVOKER_ROLE() function returns VIEW_OWNER_ROLE, use the following SQL expression: S_INVOKER_ROLE = 'VIEW_OWNER_ROLE'. You cannot call the INVOKER_ROLE() function directly - Satori calls it internally and stores its the result in the S_INVOKER_ROLE variable.
NOTE: Satori validates that the SQL expression is syntactically correct, however, the SQL expression must match the data store type the policy is used for.
Masking Semi-Structured Data
For semi-structured data granularity (e.g. a specific location inside a JSON), masking is applied to specific fields without affecting other JSON locations. To mask information inside of a JSON field in your data source, you must first ensure that the semi-structured fields are created and classified in the Data Inventory.
For example, assume you have a JSON field named attrs with the following content:
{
"customer": "John Doe",
"items": {
"SSN": "123-45-6789",
"exp": "10/01/2025",
"moresubnesting": {
"SSN": "123-45-6789",
"address": "123 Main Street"
}
}
}
If you want to mask the two SSN fields, you need to create two new entries in the data inventory under the attrs column, For example:

Satori uses JSONPath to reference field in semi-structured data types:
- The root object is denoted as
$. - Fields can be referenced using the dot notation. For example:
$.property_name. - Fields that contain special characters or spaces should be referenced using the bracket notation. For example:
$['property name']. - To reference object fields that are located in arrays, use the bracket notation with a wildcard. For example:
$.array_name[*].property_name.
Once these new entries are created, you can classify them with Satori or custom taxonomy classifiers, just like any other field. From that point forward, Security Policies and Masking Profiles will be applied automatically as defined.
For example, nested masking might work as follows:
{
"customer": "John Doe",
"items": {
"SSN": "******-6789",
"exp":"10/01/2025",
"moresubnesting": {
"SSN": "******-6789",
"address":"****treet"
}
}
}
Transformations
The following section describes how to use transformations to modify the way data is displayed or stored without changing its underlying values. Transformations are commonly used to mask sensitive information, standardize data formats, or apply custom business rules.
Generic Data Transformations
| Name | Example | Definition |
|---|---|---|
| Do Nothing | data => data |
Keeps the data as-is. |
| Hash | data => 50d858e0985ecc7f60418aaf0cc5ab587f42c2570a884095a9e8ccacd0f6545c |
Use this transformation to obfuscate the data completely while retaining its statistical properties for counting, aggregating, etc. Note: hash transformations on numeric values result in changed/different numeric values. |
| Redact | data => [REDACTED] |
Use this transformation to conceal the data. Note: Redact transformations on numeric values result in 0 (zero). |
Textual Transformations
| Name | Example | Definition |
|---|---|---|
| Mask everything except first | 12345678 => 12****** |
Use this transformation to mask the data except its first few characters. |
| Mask everything except last | 12345678 => ******78 |
Use this transformation to mask the data except its last few characters. |
| Replace characters with | 12345678 => aaaaaaaa |
Use this transformation to preserve the length of the original data but replace it with a single character string. |
| Replace entire string | 12345678 => [MASKED] |
Use this transformation to make it clear the data has been masked by replacing it with a string of your choice. |
Specific Data Transformations
In addition to the generic transformation, for selected data types specific transformation are available.
| Name | Example | Definition |
|---|---|---|
| Hash while preserving format | user@company.net => 1234@567890a.bcd |
Generates a hashed version of the original email address. Use this transformation to preserve the original format of the data |
| Mask domain | user@company.net => user@*******.*** |
Use this transformation to retain information about the username of the email address |
| Mask username | user@company.net => ****@company.net |
Use this transformation to retain information about the domain name of the email address |
| Mask while preserving format | user@company.net => ****@*******.*** |
Use this transformation to obfuscate the data completely while preserving its original format |
Credit Card
| Name | Example | Definition |
|---|---|---|
| Hash while preserving format | 1234-5678-9012-3456 => abcd-ef12-3456-7890 |
Generates a hashed version of the original credit card. Use this transformation to preserve the original format of the data |
| Mask while preserving format | 1234-5678-9012-3456 => ****-****-****-**** |
Use this transformation to obfuscate the data completely while preserving its original format |
| Show only last 4 digits | 1234-5678-9012-3456 => ****-****-****-3456 |
Shows only last 4 digits |
Date of Birth
| Name | Example | Definition |
|---|---|---|
| Set to 1/1/1970 | 2/6/1975 => 1/1/1970 |
Use this transformation to replace the date with January 1st, 1970. Note: when applying this transformation on textual values, a new string representing the date will be set. When applying on date/time values, a new value representing the date will be set. |
| Show only the year | abcd 2/6/1975 abcd => *********1975***** |
Use this transformation to retain information about the year only. Note: when applying this transformation on date/time values, a new value representing the first day of the year will be set. |
Public IP Address
| Name | Example | Definition |
|---|---|---|
| Anonymize IP address | 11.20.30.1 => 11.20.0.0 |
Use this transformation to retain /16 of an IPv4 address and /64 of an IPv6 address |
| Hash while preserving format | 11.20.30.1 => ab.cd.ef.1 |
Generates a hashed version of the original IP address. Use this transformation to preserve the original format of the data |
| Mask while preserving format | 11.20.30.1 => **.**.**.* |
Use this transformation to obfuscate the data completely while preserving its original format |
Custom SQL Transformation
A custom SQL masking function is specifically designed for users who wish to create their own masking logic for example, when masking email addresses but excluding their own company’s domain from being masked.
Satori performs SQL masking function validation on the code and displays a green checkmark icon in the input field.
NOTE: Custom SQL transformation is in private preview. Contact Satori support for more details.

Usage Notes
-
The custom SQL masking function is only available for Snowflake and Databricks native integrations.
-
The custom SQL masking function must be written in a dialect supported by the data store where the masking policy is applied. Otherwise, a SQL error may occur when the function is created or executed.
-
The data type returned by the SQL masking function must match the data type of the column to which the masking policy is applied. Otherwise, a SQL error may occur when the function is created or executed. This is a common pitfall because masking rules are defined on classifiers, and classifiers can be applied to columns of any data type.
-
Use the
dataidentifier to reference the column to which the masking policy is applied. -
Custom SQL masking functions are executed by the data store whenever the columns they are applied to are queried. Consider your function’s complexity and how it may impact query performance.
Snowflake Example 1
To mask email addresses except for addresses of the @acme.com domain, use the following custom SQL function on a textual column in Snowflake:
case
when endswith(data, '@acme.com') then data
else '***'
end
Databricks Example 2
To mask salary information based on its value, use the following custom SQL function on a numeric column in Databricks:
case
when data < 10 then 1
when data < 50 then 2
else 3
end
Snowflake Example 3
To mask non-null birth of date information to a specific date, use the following custom SQL function on a date column in Snowflake:
case
when data is null then null
else DATE('1900-01-01')
end
Considerations and Known Limitations
The following section provides you with the relevant considerations and known limitations of the dynamic masking functionality.
Supported Data Stores
Dynamic masking is not supported in AWS S3, Elasticsearch, Microsoft Fabric, MongoDB and OpenSearch.
Matching Multiple Dynamic Masking Rules
-
When more than one dynamic masking rule matches a user’s query, all masking profiles are logically merged into a single profile, this is the profile that is enforced. For example, if one profile masks emails and another masks person names and the user queries both emails and person names, both will be masked.
-
When more than one data transformation matches a user’s query, the more specific transformation will be selected. For example, if a profile transforms both PII and Email and the user queries both emails and person names, then the PII transformation is used for a persons names and the email transformation will be used for emails.
-
When multiple masking profiles match a user’s query, more than one data transformation of the same specificity can match a user’s query. In this case, Satori orders the profiles alphanumerically by their names in ascending order and selects the first one.
Dynamic Masking for Semi-structured Data
Dynamic masking for semi-structured data is only available for data stores that use the proxy-based integration.
Conditional Masking
-
Conditional masking is only available in the Databricks and Snowflake Native integrations.
-
When specifying an SQL expression for conditional masking, Satori expects the columns that are used for calculating the condition to have the same data types in all relevant tables. For example, when using the condition country is not NULL, Satori expects that the country column in all the tables that are included in the dataset to have the same data type (in the case of this example, it can be either a numeric or a textual data type).
-
The SQL expression used for conditional masking cannot reference the column being masked, only other columns in the table. For example, when masking the
countrycolumn, you cannot use the following SQL expression:country != 'US'. -
Some changes to the SQL expression for conditoinal masking may result in a short period of time where masking is not applied.
-
Satori considers all non-qualified columns in the SQL expression for conditional masking as columns that are used for calculating the masking condition. For example, consider the following SQL expression used to mask PII in an employees table only for rows representing employees who earn more than their department's avergae. The
salaryanddepartment_idcolumns are unqualified (i.e. no schema or database specified) therefore, they are expected to exist in the employees table.
salary > (
SELECT departments.average_salary
FROM departments
WHERE department_id != departments.id
)