What you will be able to do
- Tell Dynamic Data Masking apart from External Tokenization, and explain where each one changes the data
- Set up masking-policy privileges with RBAC so that policy administrators are separate from object owners
- Use DDL to create, apply, update, unset and monitor masking policies, and fix the common errors
- Decide when a projection policy fits better than a masking policy, and what a projection policy cannot protect
Key concept
Masking policy (query-time protection) — A masking policy is a schema-level object attached to a column. At query time it decides whether the caller sees the real value, a partly masked value or a fully masked value. The stored data never changes, so the protection comes entirely from evaluating the policy at runtime.
1.Two features, one policy object
In Snowflake, column-level security means attaching a masking policy to a column in a table or view. Two features use this mechanism. Dynamic Data Masking stores plain text and masks it when a query runs. External Tokenization works the other way round: a third-party provider tokenizes the data before it is loaded, and Snowflake detokenizes it at query time for callers who are authorized.
Both features use the same policy structure, with one difference. A tokenization policy must call an external function in its body. That function makes a REST call to the tokenization provider, which applies its own tokenization policy and returns either the token or the clear value. Because of this, roles must be mapped in both Snowflake and the provider.
-- Dynamic Data Masking
CREATE MASKING POLICY employee_ssn_mask AS (val string) RETURNS string ->
CASE
WHEN CURRENT_ROLE() IN ('PAYROLL') THEN val
ELSE '******'
END;
-- External Tokenization
CREATE MASKING POLICY employee_ssn_detokenize AS (val string) RETURNS string ->
CASE
WHEN CURRENT_ROLE() IN ('PAYROLL') THEN ssn_unprotect(VAL)
ELSE val -- sees tokenized data
END;The way the rewrite works is important. Snowflake does not mask only the output. It replaces the column with the policy expression wherever the column appears in the query. For an unauthorized role, joins, filters, groupings and sorts all run on the masked value. A join on a masked column can therefore return no matches for one role and the correct matches for another.
Secure views could also hide columns, so why use policies? Secure views need many views and dashboards to manage, and they do not separate the people who protect the data from the people who own it. Masking policies avoid both problems.
Checkpoint 1 of 8· Match them up
Match each mechanism to its defining behaviour
Tap a term, then the definition that fits it.
Both masking features use masking policies. Tokenization adds an external function call to the provider. Secure views are the older approach, and they lack segregation of duties.
“Secure views do not have SoD, which is a profound limitation to their utility.”Source: docs.snowflake.com
2.Masking policies with RBAC: separating policy admins from owners
Masking policies work alongside role-based access control. The usual pattern is a custom role, such as MASKING_ADMIN, held by a security or privacy officer. The officer decides which columns are protected. The table owner does not. By default, object owners cannot unset masking policies and cannot see the masked data. A policy can also stop ACCOUNTADMIN and SECURITYADMIN from viewing data they do not need.
Three privileges control this:
| Privilege | Granted on | What it controls |
|---|---|---|
| CREATE MASKING POLICY | Schema | Who can create masking policies |
| APPLY MASKING POLICY | Account | Who can set or unset policies on columns. ACCOUNTADMIN has it by default |
| APPLY | Masking policy | Optional. Lets the policy owner hand the set and unset operations for one policy to object owners |
use role securityadmin; GRANT CREATE MASKING POLICY on SCHEMA <db_name.schema_name> to ROLE masking_admin; GRANT APPLY MASKING POLICY on ACCOUNT to ROLE masking_admin;The per-policy APPLY privilege is what makes a decentralized or hybrid model possible. Policy administrators write the policies, then let trusted object owners attach them, acting as data stewards. With hundreds of teams, the central team does not have to apply every policy itself.
Inside the policy body, RBAC appears as conditions:
- CURRENT_ROLE() checks the active role.
- IS_ROLE_IN_SESSION takes the role hierarchy into account.
- INVOKER_ROLE is used for views.
- INVOKER_SHARE is used for shares.
A policy can also look up a custom entitlement table. If it uses a subquery, wrap it in EXISTS. A memoizable function can cache that lookup.
Checkpoint 2 of 8· Put it in order
Put the high-level steps for setting up Dynamic Data Masking in order
- 1.Run queries. Snowflake rewrites them and applies the policy expression
- 2.The officer creates masking policies and applies them to sensitive columns
- 3.Grant the custom role to the appropriate users
- 4.Grant masking policy management privileges to a custom role for a security or privacy officer
Privileges go to a custom role first. The role is then granted to the officer, who writes and applies the policies. Masking only takes effect when queries run.
“Grant masking policy management privileges to a custom role for a security or privacy officer.”Source: docs.snowflake.com
Checkpoint 3 of 8· Check yourself
A policy administrator wants the TABLE_OWNER role to attach and detach ssn_mask on its own tables, without giving that role account-wide power over every policy. What should they grant?
The policy-level APPLY privilege delegates the set and unset operations for one policy to object owners. The account-level privilege covers all policies, and OWNERSHIP would give away control of the policy itself.
“used by a policy owner to decentralize the [un]set operations of a given masking policy on columns to the object owners”Source: docs.snowflake.com
Checkpoint 4 of 8· Exam question
A payment_card_number column arrives already tokenized by a third-party vault before it is loaded into Snowflake, and the support team occasionally needs the real card number revealed for verified customer calls. Which design correctly combines column-level security mechanisms for this requirement?
Correct answer: A — Apply a masking policy that calls an external function to detokenize the value, combining tokenization with masking for the roles allowed to see it.
- A. External tokenization keeps the sensitive value outside Snowflake as an opaque token, and a masking policy that invokes an external function to detokenize it at query time is exactly the documented pattern for combining the two so only authorized roles see the real value.
- B. Storing a decryption key alongside the tokenized data in the same table defeats the purpose of external tokenization, since anyone with column access could reconstruct the original value without ever calling the vault's detokenization service.
- C. A projection policy that blocks the column for everyone permanently would prevent the support team from ever recovering the real card number, which contradicts the stated requirement to reveal it for verified calls.
- D. Table-level SELECT grants control access to the whole table, not to whether a specific column shows the token or the detokenized value, so RBAC alone cannot satisfy the need for conditional column-level protection.
3.Managing masking policies with DDL, and the best practices
These commands manage masking policies:
- CREATE MASKING POLICY
- ALTER MASKING POLICY
- DROP MASKING POLICY
- SHOW MASKING POLICIES
- DESCRIBE MASKING POLICY
To attach a policy, use ALTER TABLE or ALTER VIEW with MODIFY COLUMN ... SET MASKING POLICY. You can also attach it when you create the table or view. To change a policy's logic, read the current definition with GET_DDL or DESCRIBE, then run ALTER MASKING POLICY. You do not need to unset the policy first, so the column stays protected while you edit it.
Checkpoint 5 of 8· Fill the gap
Which keyword attaches the policy to the column?
ALTER TABLE IF EXISTS user_info MODIFY COLUMN email ? MASKING POLICY email_mask;Masking policies are attached with MODIFY COLUMN ... SET MASKING POLICY and detached with UNSET. ADD ... ON is the syntax for row access policies on tables.
Source: docs.snowflake.comMost masking-policy questions on the exam come down to a few lifecycle rules. A column can have only one masking policy at a time. A policy's input and output data types must match, so to mask a timestamp you return a made-up timestamp rather than a string. If a policy is still attached anywhere, you cannot drop it. Unset it first.
| Situation | Fix |
|---|---|
| DROP MASKING POLICY fails because the policy is associated with entities | UNSET it with ALTER TABLE/VIEW ... MODIFY COLUMN, then run DROP again |
| A column is already attached to another masking policy | Choose one policy. A column cannot have more than one |
| Argument and return type mismatch | Recreate the policy so the input and output types match |
| ALTER ... IF EXISTS reports success but nothing changed | Remove IF EXISTS and run the statement again |
| A policy on a virtual column or a materialized view column | Apply the policy to the column in the source table |
Best practices that the documentation calls out:
- Write a policy once and apply it to every matching column. - Be careful with hash-based masks such as SHA2, because hashes can collide. - In conditional policies that read other columns, keep the number of column arguments small, because every argument is evaluated at runtime.
For auditing, the Account Usage MASKING_POLICIES view lists every policy, and POLICY_REFERENCES shows where each one is set. To find which policy affected a particular query, look in the Query Profile. QUERY_HISTORY does not record policy names.
Checkpoint 6 of 8· Check yourself
An engineer runs ALTER MASKING POLICY IF EXISTS email_mask SET BODY -> .... Snowflake replies 'Statement executed successfully', but queries still show the old masking behaviour. What is the documented fix?
This is a documented case where IF EXISTS hides the failure. Unsetting is not needed, because ALTER MASKING POLICY works while the policy stays attached.
“Remove IF EXISTS from the ALTER statement and try again.”Source: docs.snowflake.com
Checkpoint 7 of 8· Exam question
A governance team wants a `national_id` column to never appear in any query result for analyst roles, including inside WHERE clauses or SELECT * output, rather than showing a masked placeholder value. Which control fits this requirement?
Correct answer: A — Apply a projection policy to the column, since projection policies block a column from being returned in query output for roles the policy denies.
- A. Projection policies are designed to stop a column from being projected at all for restricted roles, so the column simply does not appear in the output rather than showing a substitute value, matching the requirement exactly.
- B. A masking policy still lets the column be projected; it only substitutes NULL or another value in place of the real one, so the column would still appear in output rather than being absent as required.
- C. Row access policies filter which rows are visible based on a boolean expression, but they do not control whether a particular column can be projected, so this would not hide the column itself.
- D. Revoking table-level SELECT would block the analyst role from the whole table rather than only this one column, which is broader than the stated requirement to hide a single column.
4.Projection policies: use a column without showing it
A masking policy changes the value that users see. A projection policy decides whether a column can appear in the final output at all. A column with a projection policy is called projection constrained. If the caller's active role does not meet the policy condition, the column comes back as NULL in the outer query. It also cannot be inserted into another table or passed to an external function or stored procedure. A column can have only one projection policy.
The point of a projection policy is that the column can still be used. It works in inner queries, in WHERE filters and as a join key, so analysts can work with the real values without seeing them. This is more flexible than working with a masked or tokenized value, and it is designed for sharing data with partners.
Checkpoint 8 of 8· Check yourself
t_protected.email has a projection policy, and t_unprotected.email has none. A partner joins the two tables on email and selects t_unprotected.email. What happens?
The projection policy only stops the protected column itself from being projected. Selecting the same value from an unprotected table on the other side of the join reveals it.
“nothing prevents the user from projecting values from the column in the unprotected table.”Source: docs.snowflake.com
That flexibility comes with limits:
- A projection policy does not stop anyone from targeting an individual with a WHERE filter. - It cannot be tag-based, and it cannot go on a virtual column. - Its body cannot reference a column that has a masking policy or a table that has a row access policy. - A determined attacker can still extract values with carefully built queries.
Use projection policies with partners you already trust. If a column must never leak, remove it from the data or use differential privacy.
How it fits with other policies. A projection policy does not exclude a masking policy or a row access policy. The same column can carry a projection policy and a masking policy, and the table can carry a row access policy. The restriction is only on the projection policy body, which cannot reference a masked column or a row-access-protected table.
When a projection policy has to be swapped for another, the FORCE keyword on the column clause of ALTER TABLE replaces the masking or projection policy currently set on the column in a single statement. Projection policies also interact with aggregation policies. They are enforced after aggregation policies, whereas masking policies are enforced before them. If a column is protected by a projection policy, a query against an aggregation-constrained table cannot use that column as an argument of the COUNT function.
Exam traps
Each one states something that sounds right. Open it to see what is actually true.
1.The role that owns a table can always see its unmasked data and can remove a masking policy from it.Why is that wrong?
Masking policies separate duties. By default, object owners can neither unset the policy nor see the protected column data.
Covered in Masking policies with RBAC: separating policy admins from owners
2.DROP MASKING POLICY removes the policy from every column it is attached to.Why is that wrong?
The drop fails while any column still uses the policy. Unset it with ALTER TABLE/VIEW ... MODIFY COLUMN first, then drop it.
Covered in Managing masking policies with DDL, and the best practices
3.A projection policy stops users from learning anything about the column's values.Why is that wrong?
Hidden columns still work in inner queries and WHERE clauses, so filters and joins can reveal information.
Covered in Projection policies: use a column without showing it
Sources
Every claim above is drawn from one of these pages, quoted as it was written on the date shown.
- 1.
“External Tokenization enables accounts to tokenize data before loading it into Snowflake and detokenize the data at query runtime.”
↩︎ Two features, one policy object“masking policies for External Tokenization require using Writing external functions in the masking policy body.”
↩︎ Two features, one policy object“Note that role mapping must exist in Snowflake and the tokenization provider”
↩︎ Two features, one policy object“Masking policies solve this management challenge by avoiding an explosion of views and dashboards to manage.”
↩︎ Two features, one policy object“You can use the context functions INVOKER_ROLE and INVOKER_SHARE for use with views and shares, respectively.”
↩︎ Masking policies with RBAC: separating policy admins from owners“This command does not require unsetting a masking policy from a column, if the masking policy is set on a column.”
↩︎ Managing masking policies with DDL, and the best practices“Minimize the number of column arguments in the policy definition.”
↩︎ Managing masking policies with DDL, and the best practices“sensitive data in Snowflake is not modified in an existing table (i.e. no static masking)”
↩︎ Key concept“Object owners (i.e. the role that has the OWNERSHIP privilege on the object) do not have the privilege to unset masking policies.”
↩︎ Exam trap 1“Secure views do not have SoD, which is a profound limitation to their utility.”
↩︎ Checkpoint - 2.
“The column rewrite occurs at every place where the column specified in the masking policy appears in the query”
↩︎ Two features, one policy object“A security or privacy officer should serve as the masking policy administrator (i.e. custom role: MASKING_ADMIN)”
↩︎ Masking policies with RBAC: separating policy admins from owners“This privilege controls who can [un]set masking policies on columns and is granted to the ACCOUNTADMIN role by default.”
↩︎ Masking policies with RBAC: separating policy admins from owners“Always use EXISTS when including a subquery in the masking policy body.”
↩︎ Masking policies with RBAC: separating policy admins from owners“the input and output data types must match.”
↩︎ Managing masking policies with DDL, and the best practices“Using a hashing function in a masking policy may result in collisions; therefore, exercise caution with this approach.”
↩︎ Managing masking policies with DDL, and the best practices“(e.g. projections, join predicate, where clause predicate, order by, and group by)”
↩︎ Prediction“Grant masking policy management privileges to a custom role for a security or privacy officer.”
↩︎ Checkpoint“used by a policy owner to decentralize the [un]set operations of a given masking policy on columns to the object owners”
↩︎ Checkpoint - 3.
“can prohibit privileged users with the ACCOUNTADMIN or SECURITYADMIN role from unnecessarily viewing data.”
↩︎ Masking policies with RBAC: separating policy admins from owners“A column cannot be attached to multiple masking policies.”
↩︎ Managing masking policies with DDL, and the best practices“Masking policy names are not included in the QUERY_HISTORY view.”
↩︎ Managing masking policies with DDL, and the best practices“The POLICY_REFERENCES view provides a list of all objects in which a masking policy is set.”
↩︎ Managing masking policies with DDL, and the best practices“statement to UNSET the policy first, then try the DROP statement again.”
↩︎ Exam trap 2“Remove IF EXISTS from the ALTER statement and try again.”
↩︎ Checkpoint - 4.
“A projection policy is a first-class, schema-level object that defines whether a column can be projected in the output of a SQL query result.”
↩︎ Projection policies: use a column without showing it“Projection policies are ignored in a nested query.”
↩︎ Projection policies: use a column without showing it“A column can only have one projection policy assigned to it at any given time.”
↩︎ Projection policies: use a column without showing it“A projection policy body cannot reference a column protected by a masking policy or a table protected by a row access policy.”
↩︎ Projection policies: use a column without showing it“Projection policies are best suited for use with partners and customers with whom you have an existing level of trust.”
↩︎ Projection policies: use a column without showing it“you should omit the column entirely from your data, or employ differential privacy.”
↩︎ Projection policies: use a column without showing it“a projection constrained column can also be protected by a masking policy”
↩︎ Projection policies: use a column without showing it“columns that are hidden by projection policies can still be used in inner queries or in WHERE clauses”
↩︎ Exam trap 3“nothing prevents the user from projecting values from the column in the unprotected table.”
↩︎ Checkpoint - 5.
“Replaces a masking or projection policy that is currently set on a column with a different policy in a single statement.”
↩︎ Projection policies: use a column without showing it - 6.
“Projection policies are enforced after aggregation policies.”
↩︎ Projection policies: use a column without showing it“Masking policies are enforced before aggregation policies.”
↩︎ Projection policies: use a column without showing it - 7.
“a query against that table cannot use the column as an argument of the COUNT function.”
↩︎ Projection policies: use a column without showing it