Custom Data Masking and Masking Extensibility
Custom data masking and extensibility functions enable ALTR customers to customize the behavior of ALTR-enforced masking policies through a post-hook function. This can be used to alter the transformation applied to result sets, modify the criteria determining data access, or integrating third-party decryption or detokenization systems with masking results. Extensibility functions can also be used to modify roll behavior. ALTR customers using the custom role functionality can modify which role(s) ALTR uses to execute access control decisions.
ALTR Extensibility Functions
ALTR's Snowflake Extension Functions are user-defined functions (UDFs) that allow customers to modify the default behavior of masking policies. For custom masking, these functions take the governance decision, also known as a "disposition," from ALTR and mask the data with the default value replacement logic for that disposition. Use the UDF to change masking behavior, such as performing different string replacements or calling third party decryption functions. For custom roles, these functions can invoke Snowflake Context Functions, such as current_role, secondary_roles, and all_available_roles and returns the list of User Groups ALTR will evaluate in the access control decision.
Warning
Modifying the default masking behavior or role list may result in undefined or inconsistent behavior in query results. ALTR does not manage or guarantee the behavior of any customized masking logic.
When connecting the first column for a database to ALTR, ALTR creates a UDF named ALTR_HOOK_POST in the ALTR_DSAAS schema for that column. Whenever masking policies are made for columns, they reference this ALTR_HOOK_POST function. These UDFs are data source-specific; a new function is made for every database.
ALTR_HOOK_POST takes in a variety of parameters, including the value for each column accessed and the disposition returned by ALTR. By default, the ALTR_HOOK_POST function returns NULL, indicating that the default replacement for the disposition should be used. Updating the function to return values other than NULL will change the masking applied to result sets.
When you disconnect a database from ALTR, the ALTR_HOOK_POST is automatically removed.
When custom roles is enabled, ALTR creates a UDF that is invoked by the masking policy. This UDF take in no parameters for column-based masking policies but takes in some tag metadata parameters for tag-based masking policies. Users can use this UDF to trigger Snowflake Context Functions to obtain and pass a single role, list of roles, or custom roles to ALTR, which will be matched against ALTR user groups for the resulting governance decision. ALTR supports up to 500 roles for a single query. If more than 500 roles are sent, ALTR returns an exception to Snowflake.
For column-based policies, custom role functions are created when connecting columns if an organization-wide variable is set. Contact ALTR Support to enable this variable for your ALTR organization. For tag-based policies, custom role functions are created if an optional flag is set when connecting a tag to ALTR.
How to Use the ALTR Post-Hook Function
To use an extension function, you must replace the default NULL UDF and replace it with your own custom code. This must be done in the ALTR_DSAAS schema for the relevant database. If you have more than one database, then you must perform this operation for each database.
Caution
If using the post-hook function, extending the default masking policy may impact query performance. ALTR does not guarantee the behavior of any customized masking policy.
Post-Hook UDF Parameters
Parameter | Description |
---|---|
SCHEM | The name of the schema that the column is contained in |
TBL | The name of the table that the column is contained in |
COL | The name of the column |
ALTR_DISPOSITION | A signal from ALTR of level of access granted on the column based on the policy set in |
ALTRVAL | The plain-text value in the column |
Potential ALTR Dispositions and Default Behaviors
ALTR Disposition | Description |
---|---|
A | Allow access to plain text data (Role has "No Mask" in Lock) |
TNO_PERMISSION | Replace the value with NULLs (Role is not in a Lock) |
TBLOCKED | Replace the value with NULLs due to a threshold that has been breached. A lock is required and a threshold must be present. |
S | Return a static mask according to ALTR’s static masking behavior (Role has "Constant Mask" in Lock) |
* | Return *s for the length of original value (Role has "Full Mask" in a Lock) |
4 | Return the last 4 characters of the value and replaces characters before that with *s (Role has "Show Last Four" in a Lock) |
@ | Return the last 4 characters of the value and replaces characters before that with *s (Role has "Show Last Four" in a Lock) |
TROLE_LIMIT | Throws an error if the number of available roles to the user is above the amount that ALTR will process |
D | Throws an error if the number of available roles to the user is above the amount that ALTR will process |
Column-based Post-Hook UDF Examples
Example 1: When ALTR indicates the ALTR_DISPOSITION is a '4', (which means the default behavior is to mask everything but the last 4 characters of the value), then this behavior of the ALTR_POST_HOOK function will mask everything but the last 6 characters of the value
//show last 6 CREATE OR REPLACE FUNCTION ALTR_DSAAS>ALTR_HOOK_POST(DB VARCHAR, SCHEM VARCHAR, TBL VARCHAR, COL VARCHAR, ALTR_DISPOSITION VARCHAR, VAL VARIANT) RETURNS VARCHAR CALLED ON NULL INPUT VOLATILE COMMENT = 'Overridden' AS $$ CASE ALTR_DISPOSITION WHEN '4' THEN CASE WHEN LEN(VAL) <= 6 THEN VAL ELSE REGEXP_REPLACE(LEFT(VAL, LEN(VAL) - 6), '[^\x00-\x2F\x3A-\x40\x5B-\x60\x7B-\x7F]', '*') || RIGHT(VAL, 6)::VARCHAR END ELSE NULL END $$; GRANT OWNERSHIP ON FUNCTION ALTR_DSAAS.ALTR_HOOK_POST(VARCHAR, VARCHAR, VARCHAR, VARCHAR, VARCHAR, VARIANT) TO ROLE "ALTR_SERVICE_ROLE"; GRANT USAGE ON FUNCTION ALTR_DSAAS.ALTR_HOOK_POST(VARCHAR, VARCHAR, VARCHAR, VARCHAR, VARCHAR, VARIANT) TO ROLE "PUBLIC";
Example 2: When ALTR indicates the ALTR_DISPOSITION is a 'A' (which means the default behavior is to show the value in plain text), then this behavior of the ALTR_POST_HOOK function will call an external function to decrypt the values instead. ALTR does not create this DECRYPT external function. This must be managed by you.
//EF call CREATE OR REPLACE FUNCTION ALTR_DSAAS.ALTR_HOOK_POST(DB VARCHAR, SCHEM VARCHAR, TBL VARCHAR, COL VARCHAR, ALTR_DISPOSITION VARCHAR, VAL VARIANT ) RETURNS VARCHAR CALLED ON NULL INPUT VOLATILE COMMENT = 'Overridden' AS $$ CASE ALTR_DISPOSITION WHEN 'A' THEN PUBLIC.DECRYPT(VAL)::VARCHAR ELSE NULL END $$; GRANT OWNERSHIP ON FUNCTION ALTR_DSAAS.ALTR_HOOK_POST(VARCHAR, VARCHAR, VARCHAR, VARCHAR, VARCHAR, VARIANT) TO ROLE "ALTR_SERVICE_ROLE"; GRANT USAGE ON FUNCTION ALTR_DSAAS.ALTR_HOOK_POST(VARCHAR, VARCHAR, VARCHAR, VARCHAR, VARCHAR, VARIANT) TO ROLE "PUBLIC";
Example 3: This shows a user-defined function that contains masking policy behavior. Next, the ALTR_POST_HOOK function calls that UDF. The purpose of this is so you don't have to define masking policy behavior on every single database. You just have to create a function that contains the masking policy behavior and then call it from the ALTR_POST_HOOK function.
In this masking policy, when ALTR indicates the ALTR_DISPOSITION is a '4', ( which means the default behavior is to mask everything but the last 4 characters of the value), then this behavior of the ALTR_POST_HOOK function will mask everything but the last 6 characters of the value.
//UDF call CREATE OR REPLACE FUNCTION PUBLIC.MASKING_FUNCTION(DB VARCHAR, SCHEM VARCHAR, TBL VARCHAR, COL VARCHAR, ALTR_DISPOSITION VARCHAR, VAL VARIANT) RETURNS VARCHAR VOLATILE AS $$ CASE ALTR_DISPOSITION WHEN '4' THEN CASE WHEN LEN(VAL) <= 6 THEN VAL ELSE REGEXP_REPLACE(LEFT(VAL, LEN(VAL) - 6), '[^\x00-\x2F\x3A-\x40\x5B-\x60\x7B-\x7F]', '*') || RIGHT(VAL, 6)::VARCHAR END ELSE NULL END $$; GRANT USAGE ON FUNCTION PUBLIC.MASKING_FUNCTION(VARCHAR, VARCHAR, VARCHAR, VARCHAR, VARCHAR, VARIANT) TO ROLE "PUBLIC"; CREATE OR REPLACE FUNCTION ALTR_DSAAS.ALTR_HOOK_POST(DB VARCHAR, SCHEM VARCHAR, TBL VARCHAR, COL VARCHAR, ALTR_DISPOSITION VARCHAR, VAL VARIANT ) RETURNS VARCHAR CALLED ON NULL INPUT VOALTILE COMMENT = 'Overrridden' AS $$ PUBLIC.MASKING_FUNCTION(DB, SCHEM, TBL, COL, ALTR_DISPOSITION, VAL)::VARCHAR $$; GRANT OWNERSHIP ON FUNCTION ALTR_DSAAS.ALTR_HOOK_POST(VARCHAR, VARCHAR, VARCHAR, VARCHAR, VARCHAR, VARIANT) TO ROLE "ALTR_SERVICE_ROLE";
Tag-based Column-based Post-Hook UDF Examples
How to Use the ALTR Custom Roles Function
To use the custom role extension function, you must connect a column or tag to ALTR with custom roles enabled. For column-based integrations, this is done by having a feature flag for your organization activated prior to connecting a column. Reach out to ALTR Support to enable this flag. For tag-based integrations, this is done by setting an optional flag when connecting the tag to ALTR.
When a column or tag is connected with custom roles enabled, ALTR creates a new user-defined function , ALTR_ROLE_SET, in Snowflake that is invoked with the masking policy. This function is used to determine which role(s) are sent to ALTR for policy evaluation. By default, this UDF returns CURRENT_AVAILABLE_ROLES. You can customize this function to invoke Snowflake context functions to generate, limit, and otherwise customize the list of roles considered for policy evaluation. These rolls are matched against user groups defined in ALTR's access control and security policies. If more than one policy is invoked for a single query due to multiple roles being evaluated, ALTR returns the most permissive disposition.
For Custom Role functions made for tag-based masking policies, the UDF is able to take in context about the tag invoking the policy. This can be used to customize the role(s) sent to ALTR based on information on the tag that invoked the masking policy.
ALTR limits the number of values considered for roles to 500. If more than 500 roles are sent for a single query, ALTR returns an exception to Snowflake.
Custom Role UDF Examples
Note
The following are examples of custom role functions created for tag-based integrations with ALTR.
The following UDF returns the user's current role to ALTR.
CREATE OR REPLACE SECURE FUNCTION "ALTR_ROLE_SET" (TAG_DATABASE VARCHAR, TAG_SCHEMA VARCHAR, TAG_NAME VARCHAR, TAG_VALUE VARCHAR) RETURNS STRING COMMENT = 'v1.0.2. Programmatically created by ALTR to govern sensitive data. Do not drop or modify without guidance from ALTR engineering support.' AS $$ CURRENT_ROLE() $$;
The following UDF returns the user's secondary Snowflake roles to ALTR.
CREATE OR REPLACE SECURE FUNCTION "ALTR_ROLE_SET" (TAG_DATABASE VARCHAR, TAG_SCHEMA VARCHAR, TAG_NAME VARCHAR, TAG_VALUE VARCHAR) RETURNS STRING COMMENT = 'v1.0.2. Programmatically created by ALTR to govern sensitive data. Do not drop or modify without guidance from ALTR engineering support.' AS $$ CAST(STRTOK_TO_ARRAY(GET(PARSE_JSON(CURRENT_SECONDARY_ROLES()), 'roles'),',') AS VARCHAR); $$;