Access Control: RBAC, DAC, Masking Policies & Row Access
Architecture Diagram 1: Access Control Hierarchy
Architecture Diagram 2: Dynamic Data Masking Flow
Architecture Diagram 3: RBAC Role Hierarchy Tree
Architecture Diagram 4: Dynamic Data Masking Flow
Architecture Diagram 5: Row-Level Security Implementation
RBAC is Snowflake's primary access model where permissions are granted to roles, and roles are assigned to users. Users inherit all permissions from assigned roles. The hierarchy includes ACCOUNTADMIN β SECURITYADMIN β USERADMIN β SYSADMIN β custom roles, enabling centralized permission management.
Dynamic Data Masking provides column-level security by transforming data at query time based on the executing role. Unlike static masking, it preserves original data while presenting different views per role β full masking, partial masking, hashing, or nullification.
Row-Level Security (RLS) restricts which rows users can query based on role, user attributes, or session context. Row access policies return TRUE/FALSE per row, filtering entire rows from results β ensuring users only see authorized data subsets.
Limit ACCOUNTADMIN to 2β3 named users for break-glass scenarios. Use SECURITYADMIN for daily security operations. Create functional roles (analyst, developer) and organizational roles (marketing, finance) for clear separation of duties.
- RBAC: Permissions β Roles β Users (hierarchical inheritance)
- Dynamic Masking: Column-level security with role-based transformation
- Row-Level Security: Row filtering based on role/session context
- Least privilege: Grant minimum necessary permissions; audit quarterly
- Separation of duties: No single user has both administrative and operational privileges
Detailed Explanation
What is RBAC?
Role-Based Access Control is Snowflake's primary access model: permissions β roles β users.
RBAC Structure
- Predefined system hierarchy:
ACCOUNTADMINβSECURITYADMINβUSERADMINβSYSADMIN ACCOUNTADMINβ break-glass only (limit to 2β3 named users)- Custom roles inherit from system roles or other custom roles
- Supports organizational hierarchies (department β team β individual)
Discretionary Access Control (DAC)
- Object-level permissions for fine-grained scenarios
- Privileges: SELECT, INSERT, UPDATE, DELETE, USAGE
GRANT OPTIONcontrols privilege redistribution- Useful for temporary access or when RBAC is too coarse
Dynamic Data Masking
- Column-level security β transforms data at query time per role
- Zero storage overhead β original data preserved
- Masking strategies:
| Strategy | Example | Use Case |
|---|---|---|
| Full masking | *** | Highly sensitive |
| Partial masking | XX-XX-1234 | Semi-sensitive |
| Hash masking | SHA-256 | Joining on sensitive data |
| Nullification | NULL | Invisible data |
Row-Level Security (RLS)
- Filters entire rows from query results based on role/context
- Policies return TRUE/FALSE per row
- Essential for multi-tenant applications
Best Practices
- Least privilege β grant minimum necessary permissions; audit quarterly
- Role hierarchies β functional roles (analyst, developer) + organizational roles (marketing, finance)
- Separation of duties β no single user has both admin and operational privileges
- Document roles β maintain registry with purpose, users, and permissions
Key Takeaway: Limit ACCOUNTADMIN to 2β3 users. Use SECURITYADMIN for daily security operations. Audit access patterns quarterly.
Key Concepts Table
| Access Model | Granularity | Management | Use Case |
|---|---|---|---|
| RBAC | Role-level | Centralized | Organization-wide permissions |
| DAC | Object-level | Distributed | Specific access grants |
| Dynamic Masking | Column-level | Centralized | Data privacy compliance |
| Row-Level Security | Row-level | Centralized | Multi-tenant data isolation |
| System Role | Purpose | Key Privileges | Typical Users |
|---|---|---|---|
| ACCOUNTADMIN | Account management | Everything | CTO, Security Lead |
| SECURITYADMIN | Security management | Grants, Users | Security Team |
| USERADMIN | User management | Users, Roles | HR, IT Admin |
| SYSADMIN | Resource management | DB, Warehouse | Data Engineers |
| Masking Type | Data Preservation | Query Performance | Use Case |
|---|---|---|---|
| Full Masking | None | High | Highly sensitive data |
| Partial Masking | Partial | High | Semi-sensitive data |
| Hash Masking | Hash only | Medium | Joining on sensitive data |
| Null Masking | None | High | Invisible data |
Code Examples
-- Example 1: Create custom roles
CREATE ROLE analytics_role;
CREATE ROLE etl_role;
CREATE ROLE reporting_role;
-- Grant roles to parent role
GRANT ROLE analytics_role TO ROLE sysadmin;
GRANT ROLE etl_role TO ROLE sysadmin;
GRANT ROLE reporting_role TO ROLE sysadmin;
-- Example 2: Grant database privileges
GRANT USAGE ON DATABASE analytics_db TO ROLE analytics_role;
GRANT USAGE ON SCHEMA analytics_db.prod TO ROLE analytics_role;
GRANT SELECT ON ALL TABLES IN SCHEMA analytics_db.prod TO ROLE analytics_role;
-- Example 3: Create masking policy
CREATE OR REPLACE MASKING POLICY ssn_mask AS (val STRING)
RETURNS STRING ->
CASE
WHEN CURRENT_ROLE() IN ('hr_role', 'security_admin') THEN val
WHEN CURRENT_ROLE() = 'support_role' THEN 'XXX-XX-' || RIGHT(val, 4)
ELSE '***-**-****'
END;
-- Apply masking policy to column
ALTER TABLE customer_pii MODIFY COLUMN ssn SET MASKING POLICY ssn_mask;
-- Example 4: Create row access policy
CREATE OR REPLACE ROW ACCESS POLICY regional_access AS (col_region VARCHAR)
RETURNS BOOLEAN ->
CASE
WHEN CURRENT_ROLE() = 'executive_role' THEN TRUE
WHEN CURRENT_ROLE() = 'regional_mgr_role'
AND col_region = CURRENT_ACCOUNT_REGION() THEN TRUE
ELSE FALSE
END;
-- Apply row access policy to table
ALTER TABLE sales_data ADD ROW ACCESS POLICY regional_access ON (region);
-- Example 5: Grant masking policy usage
GRANT USAGE ON MASKING POLICY ssn_mask TO ROLE analytics_role;
-- Example 6: Create resource monitor with security
CREATE RESOURCE MONITOR security_monitor
WITH
CREDIT_QUOTA = 1000
FREQUENCY = MONTHLY
TRIGGERS
ON 100% DO NOTIFY
ON 100% DO SUSPEND_IMMEDIATELY;
-- Example 7: Monitor access patterns
SELECT
user_name,
role_name,
query_start_time,
query_text,
database_name,
schema_name
FROM SNOWFLAKE.ACCOUNT_USAGE.QUERY_HISTORY
WHERE query_start_time >= DATEADD(day, -7, CURRENT_TIMESTAMP())
AND role_name IN ('analytics_role', 'etl_role', 'reporting_role')
ORDER BY query_start_time DESC;
-- Example 8: Audit role assignments
SELECT
grantee_name,
granted_to,
grant_option,
created_on
FROM SNOWFLAKE.ACCOUNT_USAGE.GRANTS_TO_ROLES
WHERE deleted_on IS NULL
ORDER BY grantee_name;
-- Example 9: Dynamic masking with multiple conditions
CREATE OR REPLACE MASKING POLICY pii_mask AS (val STRING, data_type STRING)
RETURNS STRING ->
CASE
WHEN CURRENT_ROLE() = 'executive_role' THEN val
WHEN CURRENT_ROLE() = 'hr_role' AND data_type = 'ssn' THEN val
WHEN CURRENT_ROLE() = 'marketing_role' AND data_type = 'email'
THEN CONCAT(LEFT(val, 1), '***@***.com')
WHEN CURRENT_ROLE() = 'support_role' AND data_type = 'ssn'
THEN CONCAT('***-**-', RIGHT(val, 4))
WHEN CURRENT_ROLE() = 'analytics_role' THEN '***'
ELSE '***'
END;
-- Example 10: Row-level security with complex logic
CREATE OR REPLACE ROW ACCESS POLICY department_access AS (
col_department VARCHAR,
col_amount NUMBER
)
RETURNS BOOLEAN ->
CASE
WHEN CURRENT_ROLE() = 'executive_role' THEN TRUE
WHEN CURRENT_ROLE() = 'dept_head_role'
AND col_department = CURRENT_ACCOUNT_DEPARTMENT() THEN TRUE
WHEN CURRENT_ROLE() = 'analyst_role'
AND col_amount < 10000 THEN TRUE
WHEN CURRENT_ROLE() = 'team_lead_role'
AND col_department = CURRENT_ACCOUNT_DEPARTMENT()
AND col_amount < 50000 THEN TRUE
ELSE FALSE
END;
Performance Metrics
| Metric | Target | Warning | Critical |
|---|---|---|---|
| Permission Check Latency | < 1ms | 1-5ms | > 5ms |
| Masking Policy Evaluation | < 0.5ms | 0.5-2ms | > 2ms |
| Row Access Policy Evaluation | < 1ms | 1-5ms | > 5ms |
| Role Assignment Count | < 5/user | 5-10/user | > 10/user |
| Policy Count per Table | < 10 | 10-20 | > 20 |
Best Practices
-
Implement least privilege: Grant users only the minimum permissions necessary for their job functions. Regularly review and adjust permissions.
-
Use role hierarchies: Create functional roles for specific job functions and organizational roles for business units. Grant permissions to functional roles.
-
Limit ACCOUNTADMIN: Restrict ACCOUNTADMIN access to 2-3 named users for break-glass scenarios only. Use SECURITYADMIN for daily security operations.
-
Document all roles: Maintain a role registry describing each role's purpose, typical users, and granted permissions. Update documentation regularly.
-
Audit access patterns: Regularly review query history, role assignments, and permission grants to detect anomalies and ensure compliance.
-
Implement dynamic masking: Apply masking policies to sensitive columns (PII, financial data) to protect data while enabling broad access.
-
Use row-level security: Implement RLS for multi-tenant applications or scenarios where different users should see different data subsets.
-
Separate duties: Ensure no single user has both administrative and operational privileges. Implement separation of duties for critical functions.
-
Monitor security events: Set up alerts for failed login attempts, permission changes, and unusual query patterns.
-
Regular security reviews: Conduct quarterly access reviews to ensure permissions remain appropriate and compliant with policies.
See Also
- Warehouse Management β Warehouse access control
- Time Travel & Data Cloning β Data recovery with access control
- Stored Procedures β Security in stored procedures
- Snowflake Architecture β Security architecture overview
- PySpark Iceberg Tables β Data lake security patterns
- Data Warehouse Concepts β Data warehouse design principles