What you will be able to do
- Register, verify and rotate a customer-managed key for Tri-Secret Secure using the CMK system functions
- Write Dynamic Data Masking policies with CASE expressions, context functions, UDFs and entitlement subqueries
- Grant the masking privileges that separate policy administrators from object owners
- Update, replace and monitor masking policies and their effect on what users see
Key concept
Query-time policy enforcement — Snowflake's data protection policies never change the stored data. When a query runs, the policy body is evaluated in that query's context (role, account and so on), and the result decides what that user sees. Changing a policy's body therefore changes visibility immediately, everywhere the policy is attached.
1.Tri-Secret Secure: owning the customer-managed key
Tri-Secret Secure adds a key you control to Snowflake's standard encryption. A Snowflake-maintained key and a customer-managed key (CMK), which you create in the key management service (KMS) of the cloud provider hosting your account, together form a composite master key. That key acts as the account master key: it wraps the keys below it in the hierarchy, such as table master keys, and those keys derive the file keys that encrypt the data. The composite key never encrypts raw data itself.
You also take on responsibility. If the CMK is revoked, Snowflake can no longer decrypt your data. So treat the KMS key's availability as part of your data's availability. Two more constraints: if TSS is enabled, or will be, you must turn on Dedicated Storage Mode before you create hybrid tables. And for AWS external key stores, the only products Snowflake tests and supports are Thales HSMs and Thales CipherTrust Cloud Key Manager.
| Function | What it does in the flow |
|---|---|
| SYSTEM$REGISTER_CMK_INFO | Registers the CMK you created in the cloud KMS (arguments differ per cloud platform) |
| SYSTEM$GET_CMK_INFO | Shows the registered CMK and its registration and activation status; callable at any time |
| SYSTEM$GET_CMK_CONFIG | Generates the information the cloud provider needs to authorize Snowflake's access to the CMK; on Azure you must pass tenant_id |
| SYSTEM$VERIFY_CMK_INFO | Confirms connectivity between your Snowflake account and the CMK |
You do the self-registration. Snowflake Support does the activation: once the key is verified, you ask Support to enable Tri-Secret Secure for that specific account. Then read the output of SYSTEM$GET_CMK_INFO. Just after activation it reports that the CMK "is being activated", which means rekeying hasn't finished. When activation completes, it reports "is activated". To rotate or replace the CMK, repeat the same registration steps with the new key. During the change, GET_CMK_INFO reports that the key "is being rekeyed".
Checkpoint 1 of 3· Put it in order
Put the CMK self-registration steps for Tri-Secret Secure in order.
- 1.Call SYSTEM$REGISTER_CMK_INFO to register the CMK
- 2.Contact Snowflake Support to enable Tri-Secret Secure for the account
- 3.Apply the generated policy on the cloud platform to authorize the CMK
- 4.Call SYSTEM$VERIFY_CMK_INFO to confirm connectivity
- 5.Call SYSTEM$GET_CMK_CONFIG to generate the cloud provider information
- 6.Create the CMK in the cloud provider's key management service
The customer creates, registers, authorizes and verifies the key. Support activation always comes last, and it is based on the CMK you registered.
“Create the CMK. Register the CMK. Generate information for the cloud provider. Apply the KMS policy.”Source: docs.snowflake.com
Sources1
2.Designing Dynamic Data Masking policies
Data protection moves from key ownership to column-level security. Dynamic Data Masking uses masking policies to mask plain-text column data in tables and views when a query runs. A masking policy is a schema-level object made of a single data type, one or more conditions, and one or more masking functions. Write it once and you can apply it to any column with a matching data type.
CREATE OR REPLACE MASKING POLICY email_mask AS (val string) RETURNS string ->
CASE
WHEN CURRENT_ROLE() IN ('ANALYST') THEN val
ELSE '*********'
END;The body can return a value for each kind of user:
- NULL or a static string for unauthorized users. - A partial mask built with REGEXP_REPLACE. - A SHA2 hash. Hashes can collide, so use them with caution. - The output of a UDF. The role creating the policy needs USAGE on the UDF. - For VARIANT data, an OBJECT_INSERT that overwrites a single field.
The conditions can use CURRENT_ACCOUNT to unmask only in production, or SYS_CONTEXT('SNOWFLAKE$CURRENT', 'IS_AGENT_ACTIVATED') to mask whenever an AI agent is active. For entitlements kept in a table, put the subquery inside EXISTS. For a lookup on a mapping table, a MEMOIZABLE function can cache the result.
CASE WHEN EXISTS (SELECT role FROM <db>.<schema>.entitlement WHERE mask_method='unmask' AND role = current_role()) THEN val ELSE '********' END;| Privilege | Object | Purpose |
|---|---|---|
| CREATE MASKING POLICY | Schema | Controls who can create masking policies |
| APPLY MASKING POLICY | Account | Controls who can set or unset policies on columns; granted to ACCOUNTADMIN by default |
| APPLY | Masking policy | Optional; lets a policy owner hand set/unset of that one policy to object owners |
Because the rewrite covers joins and filters too, masking only one side of a comparison is a known anti-pattern: the results are not what the user intended. Masking also enforces segregation of duties by default. Object owners can't unset a masking policy, and they can't see the data in a masked column.
Checkpoint 2 of 3· Check yourself
You need to mask a TIMESTAMP_NTZ column so that unauthorized roles see the string '*MASKED*'. What happens?
Masking policy signatures need matching input and output types. The documented workaround returns a fabricated value such as DATE_FROM_PARTS(0001, 01, 01)::timestamp_ntz.
“Snowflake does not support different input and output data types in a masking policy”Source: docs.snowflake.com
3.Managing the masking policy lifecycle and watching its impact
A policy's life runs from create, to set on columns, to update, and finally to unset and drop. Updating is the easy part. Get the current body with GET_DDL or DESCRIBE MASKING POLICY, then change it with ALTER MASKING POLICY. You don't need to unset the policy first, so the columns stay protected while the definition changes. To swap a column's policy for a different one in one statement, use ALTER TABLE ... MODIFY COLUMN with the FORCE keyword.
The common errors each point to a specific fix:
- A column can carry only one masking policy. - A policy that is still attached cannot be dropped. Unset it with ALTER TABLE or ALTER VIEW ... MODIFY COLUMN first. - A policy cannot go on a virtual column or a materialized view column. Apply it to the source table instead. - ALTER ... IF EXISTS can return "Statement executed successfully" without updating anything. Remove IF EXISTS and run it again.
Every body change alters what someone can see, so check the impact deliberately:
- The Account Usage MASKING_POLICIES view catalogs all policies. - The POLICY_REFERENCES view or Information Schema table function lists every object a policy is set on. That is the reach of your change. - The Query Profile shows which policies a specific query used. The QUERY_HISTORY view does not. - Before changing a policy, call POLICY_CONTEXT to simulate a query under chosen context values, such as activated roles. - After changing it, query the column as an authorized role and then as a role like PUBLIC, and confirm the results differ as intended.
Checkpoint 3 of 3· Match them up
Match each tool to what it tells you about masking policy impact.
Tap a term, then the definition that fits it.
Each tool answers a different monitoring question. Only the Query Profile ties policy names to a particular query; QUERY_HISTORY does not.
“The masking policy names that were used in a specific query can be found in the Query Profile.”Source: docs.snowflake.com
Exam traps
Each one states something that sounds right. Open it to see what is actually true.
1.The Account Usage QUERY_HISTORY view shows which masking policies a query triggered.Why is that wrong?
QUERY_HISTORY holds only the SQL text. The policy names used by a query are in the Query Profile.
Covered in Managing the masking policy lifecycle and watching its impact
2.If ALTER MASKING POLICY IF EXISTS returns success, the policy was updated.Why is that wrong?
It can report success and change nothing. The documented fix is to remove IF EXISTS and run the statement again.
Covered in Managing the masking policy lifecycle and watching its impact
3.Snowflake can still decrypt Tri-Secret Secure data if the customer revokes the CMK.Why is that wrong?
The CMK is part of the composite master key. Revoking it makes the data undecryptable by Snowflake.
Covered in Tri-Secret Secure: owning the customer-managed key
Sources
Every claim above is drawn from one of these pages, quoted as it was written on the date shown.
- 1.
“The composite master key is never used to encrypt raw data.”
↩︎ Tri-Secret Secure: owning the customer-managed key“calling the SYSTEM$GET_CMK_INFO function returns a message that contains ...is being rekeyed....”
↩︎ Tri-Secret Secure: owning the customer-managed key“Snowflake officially tests and supports only Thales Hardware Security Modules (HSM) and Thales CipherTrust Cloud Key Manager (CCKM) data encryption products.”
↩︎ Tri-Secret Secure: owning the customer-managed key“If the customer-managed key (CMK) in the composite master key hierarchy is revoked, your data can no longer be decrypted by Snowflake.”
↩︎ Exam trap 3“Create the CMK. Register the CMK. Generate information for the cloud provider. Apply the KMS policy.”
↩︎ Checkpoint - 2.
“uses masking policies to selectively mask plain-text data in table and view columns at query time.”
↩︎ Designing Dynamic Data Masking policies“The POLICY_REFERENCES view provides a list of all objects in which a masking policy is set.”
↩︎ Managing the masking policy lifecycle and watching its impact“Masking policy names are not included in the QUERY_HISTORY view.”
↩︎ Exam trap 1“Remove IF EXISTS from the ALTER statement and try again.”
↩︎ Exam trap 2“The masking policy names that were used in a specific query can be found in the Query Profile.”
↩︎ Checkpoint - 3.
“Always use EXISTS when including a subquery in the masking policy body.”
↩︎ Designing Dynamic Data Masking policies“Snowflake does not support different input and output data types in a masking policy”
↩︎ Checkpoint - 4.
“Object owners cannot view column data in which a masking policy applies.”
↩︎ Designing Dynamic Data Masking policies“So, a column that is protected by a policy remains protected while the policy definition is being updated.”
↩︎ Managing the masking policy lifecycle and watching its impact“This means that sensitive data in Snowflake is not modified in an existing table (i.e. no static masking).”
↩︎ Key concept“A masking policy is deliberately applied wherever the relevant column is referenced by a SQL construct to prevent the de-anonymization of data”
↩︎ Prediction - 5.
“Replaces a masking or projection policy that is currently set on a column with a different policy in a single statement.”
↩︎ Managing the masking policy lifecycle and watching its impact - 6.
“Call the POLICY_CONTEXT function to simulate a query on a column that is protected by a masking policy”
↩︎ Managing the masking policy lifecycle and watching its impact