Skip to main content

Custom Data Masking and Masking Extensibility

Custom data masking and extensibility functions enable ALTR customers to customize the behavior of ALTR-enforced masking policies. 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. Users can 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 paramaters 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 will return 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.

Post-Hook UDF Parameters
  • 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
  • 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)

  • @ - Returns the domain name of an email address but replaces anything before with *s (Role has "Email Mask" 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 - A catch-all used if ALTR does not return any of the above

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";
        

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 activating a feature flag for your organization 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 will be 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 will return 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 will return an exception to Snowflake.

Custom Role UDF Examples

Note: These are example 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);
$$;