What you will be able to do
- Build an External Tokenization masking policy that detokenizes through an external function
- Protect columns at scale by assigning masking policies to tags, and predict which policy wins
- Create, attach and replace row access policies, and explain how they interact with masking
- Troubleshoot row access policy enforcement with EXPLAIN, POLICY_REFERENCES and POLICY_CONTEXT
1.External Tokenization: detokenizing through an external function
External Tokenization uses the same masking policy object as Dynamic Data Masking, but the data starts out differently. A third-party tokenization provider tokenizes the data before it is loaded, so Snowflake stores only tokens. Users never see the real values, even on a column that has no policy. The masking policy body calls an external function. At query runtime that function makes a REST API call to the provider, which evaluates its own tokenization policy and returns tokenized or detokenized data. Role mapping must exist on both sides, in Snowflake and at the provider.
create or replace masking policy email_de_token as (val string) returns string ->
case
when current_role() in ('ANALYST') then de_email(val)
else val
end;The privileges extend past the masking policy itself. The policy owner needs USAGE on the external function, and the function's owner needs USAGE on the API integration it references. Users who query the column need neither. External functions are available in every edition, but integrating your tokenization provider with Snowflake External Tokenization requires Enterprise Edition or higher. Because external functions can't run in the context of a share, mask shared data with Dynamic Data Masking instead.
Checkpoint 1 of 5· Check yourself
Which statement about External Tokenization is correct?
Data sharing applies only to Dynamic Data Masking. Tokenization happens at the provider before loading, the USAGE grants apply only to the policy and function owners, and the integration needs Enterprise Edition or higher.
“Data sharing only applies to Dynamic Data Masking because external functions cannot be invoked in the context of a share.”Source: docs.snowflake.com
2.Tag-based masking: one assignment, many columns
Attaching policies column by column doesn't scale. A tag-based masking policy instead sets the masking policy on a tag with ALTER TAG. Any column carrying that tag, directly or through tag inheritance, is protected when its data type matches the policy signature. A tag holds at most one masking policy per data type, so a common pattern is one generic policy each for STRING, NUMBER and TIMESTAMP_LTZ. Policy bodies can also read the tag's string value through SYSTEM$GET_TAG_ON_CURRENT_COLUMN or SYSTEM$GET_TAG_ON_CURRENT_TABLE.
ALTER TAG security SET MASKING POLICY my_masking_policy;Where you set the tag decides the coverage. On a database or schema, columns in newly added tables and views are protected automatically. On a table, the tag covers all of its columns, including columns added later, much like future grants.
The privileges differ by level. For a database or schema, the role needs global APPLY MASKING POLICY plus either global APPLY TAG or, if it owns the schema, APPLY on the tag. For tables and views, global APPLY MASKING POLICY is enough. A role with OWNERSHIP or APPLY on an already-policied tag can tag its own tables without APPLY MASKING POLICY.
Limits to remember:
- A masking policy can't be assigned to a system tag. - While the policy is assigned, neither the tag nor the policy (nor their parent schema and database) can be dropped. - A materialized view can't be created on a table protected by a tag-based policy. - A table moved or cloned into another schema loses protection from the source schema's tag-based policy.
Checkpoint 2 of 5· Check yourself
A STRING column has a masking policy assigned directly. Its table also carries a tag with a STRING masking policy. Which policy masks the column?
A column can be covered by both. When that happens, the policy assigned directly to the column takes precedence over the tag-based one.
“the masking policy that is directly assigned to the column takes precedence over the tag-based masking policy.”Source: docs.snowflake.com
Sources3
3.Row access policies: design, attachment and precedence
Masking controls what a value looks like. A row access policy controls whether the row appears at all. The body returns BOOLEAN for each row. Its conditions can check the user's active primary and secondary roles directly, look roles up in a mapping table, or do both. Snowflake recommends IS_ROLE_IN_SESSION for account roles and IS_DATABASE_ROLE_IN_SESSION for database roles. Keep a centralized mapping table in the same database as the protected table.
You attach the policy to the columns named in its signature, either with CREATE TABLE/VIEW ... WITH ROW ACCESS POLICY or with ALTER TABLE/VIEW. A table or view can have only one row access policy at a time. To replace one, drop the old policy and add the new one in the same statement:
ALTER VIEW v1
DROP ROW ACCESS POLICY rap_v1_version_1,
ADD ROW ACCESS POLICY rap_v1_version_2 ON (empl_id);When an object has a row access policy and masking policies, the row access policy is evaluated first. A column can appear in a row access policy signature or in a masking policy signature, but not in both. Adding a policy fails if its body refers to a column or table that another policy already protects. A materialized view can't be created on a table that has a row access policy, and a row access policy can't be added to a table that already has a materialized view built on it. The table below shows what happens when you derive a new table from a protected one.
| Statement | Result |
|---|---|
| CREATE TABLE ... CLONE | The clone maps to the same policies; with a schema clone, local references point to the cloned policy |
| CREATE TABLE ... LIKE | No row access policy on the new table, which is empty |
| CREATE TABLE ... AS SELECT | The new table holds only the filtered rows and has no row access policy |
Checkpoint 3 of 5· Fill the gap
Which keyword attaches a row access policy to an existing table?
ALTER TABLE t1 ? ROW ACCESS POLICY rap_t1 ON (empl_id);Row access policies are attached with ADD ROW ACCESS POLICY ... ON (columns) and removed with DROP. SET ... MASKING POLICY is the column-level masking syntax.
Source: docs.snowflake.comCheckpoint 4 of 5· Exam question
Support agents should see the full `email` value only when the row's `region` column equals 'EU'; everyone else must see a masked value. A masking policy `mask_email(val STRING, region STRING)` already exists. Which statement attaches it correctly?
Correct answer: A — ALTER TABLE customers MODIFY COLUMN email SET MASKING POLICY mask_email USING (email, region);
- A. Correct: conditional masking passes the protected column first and the additional columns after it in the USING clause, matching the policy signature.
- B. Masking policies have no WITH CONDITION clause. The condition belongs inside the policy body, and extra columns are passed with USING.
- C. The ADD ... ON (...) form is the syntax for row access policies. Masking policies are set on a column with SET MASKING POLICY.
- D. A column can carry only one masking policy and the policy takes both columns as arguments. Attaching it separately to region would protect the wrong column and mismatch the signature.
4.Troubleshooting row access policy enforcement
When a user says rows are missing, or rows are visible that shouldn't be, start by confirming the policy is in play:
- Run EXPLAIN on the query. At runtime Snowflake creates a dynamic secure view, so plan steps that invoke the policy show DynamicSecureView in the operation column and "<object_name> (+ RowAccessPolicy)" in the object column. Neither EXPLAIN nor Query History reveals the policy's name, signature or expression. - Call POLICY_REFERENCES, with either the policy name or an object name and domain, to see what is attached where. - Call POLICY_CONTEXT to replay the query as a particular role or context, without signing in as that user.
Then check the usual causes:
- CURRENT_DATABASE and CURRENT_SCHEMA inside a policy body refer to the protected table's location, not the session's. - Tag-based row access policies need the argument names and types to match the table's columns. Without a resolving signature alias, the policy is not enforced. - A row access policy assigned directly to the object overrides one inherited from a tag. - An external table can't serve as a mapping table, because database clones don't copy it.
Checkpoint 5 of 5· Check yourself
Analysts want to confirm a row access policy is filtering their query, but they have no access to policy metadata. What will they see?
EXPLAIN flags each step that invokes a row access policy with DynamicSecureView and "(+ RowAccessPolicy)". Snowflake shows nothing else about the policy itself.
“The operation column includes the value DynamicSecureView.”Source: docs.snowflake.com
Exam traps
Each one states something that sounds right. Open it to see what is actually true.
1.Masking policies run first, and the row access policy then filters the masked rows.Why is that wrong?
The order is the reverse: the row access policy is evaluated before any masking policies on the object.
Covered in Row access policies: design, attachment and precedence
2.A tag-based row access policy overrides a row access policy assigned directly to the table.Why is that wrong?
Just as with masking, the directly assigned row access policy wins over the one assigned through a tag.
Sources
Every claim above is drawn from one of these pages, quoted as it was written on the date shown.
- 1.
“Tokenization is the process of removing sensitive data by replacing it with an undecipherable token.”
↩︎ External Tokenization: detokenizing through an external function“are not necessary for the role of the user querying the column with a masking policy.”
↩︎ External Tokenization: detokenizing through an external function“if you choose to integrate your tokenization provider with Snowflake External Tokenization, you must upgrade to Enterprise Edition or higher.”
↩︎ External Tokenization: detokenizing through an external function - 2.
“At query runtime, Snowflake uses the external function to make a REST API call to the tokenization provider”
↩︎ External Tokenization: detokenizing through an external function“Data sharing only applies to Dynamic Data Masking because external functions cannot be invoked in the context of a share.”
↩︎ Checkpoint - 3.
“When the data type in the masking policy signature and the data type of the column match, the tagged column is automatically protected”
↩︎ Tag-based masking: one assignment, many columns“Assigning a tag-based masking policy to a table automatically applies the masking policy to any new table columns.”
↩︎ Tag-based masking: one assignment, many columns“A masking policy cannot be assigned to a system tag.”
↩︎ Tag-based masking: one assignment, many columns“the masking policy that is directly assigned to the column takes precedence over the tag-based masking policy.”
↩︎ Checkpoint - 4.
“A table or view can only be protected by one row access policy at a time.”
↩︎ Row access policies: design, attachment and precedence - 5.
“the same column cannot be specified in both a row access policy signature and a masking policy signature at the same time.”
↩︎ Row access policies: design, attachment and precedence“Snowflake recommends writing the policy conditions to call the IS_ROLE_IN_SESSION or the IS_DATABASE_ROLE_IN_SESSION function”
↩︎ Row access policies: design, attachment and precedence“Snowflake does not show users any row access policy information (i.e. policy name, policy signature, policy expression) or the objects accessed by the policy.”
↩︎ Troubleshooting row access policy enforcement“the function returns the database or schema that contains the protected table, not the database or schema in use for the session.”
↩︎ Troubleshooting row access policy enforcement“When a database object has both a row access policy and one or more masking policies, Snowflake evaluates the row access policy first.”
↩︎ Exam trap 1“The operation column includes the value DynamicSecureView.”
↩︎ Checkpoint - 6.
“By default, the name and data type of each policy argument must match a column on the table (case insensitive).”
↩︎ Troubleshooting row access policy enforcement“the directly assigned row access policy takes precedence over the row access policy assigned to the tag.”
↩︎ Exam trap 2