Basic Policy Examples
Block All Connections by Default
A foundational security pattern: deny all access by default.Allow Only Admin Group
Combine default deny with explicit allow for specific groups.Allow Machine User with End-User in Admin Group
Control access based on both the machine user and the actual person using it.Block Writes to Production
Prevent destructive operations in production environments.Advanced Scenarios
Redact emails on Snowflake
Let’s say we want to redact all email columns that come back from our Snowflake database. As you will see, we are going to do this with multiple policies. The main reason is that a single policy can only return onepre_request or post_request stage at a time. Additionally, using separate policies allows for customized notification settings per policy.
Policy 1 – Mask emails
Let’s start by writing our first policy that will mask email columns.CONCAT), because we can’t guarantee the output format. Let’s write a second policy to handle this.
Policy 2 – Hash function outputs
Here is a policy that will hash email columns that have been through a function, unless that function was an aggregate.ARRAY_AGG in Snowflake would allow someone to view the emails.
Policy 3 – Block queries that may try to escape our masking
What if an attacker tries to bypass our masking policy with advanced Snowflake features? Let’s block the query if we detect them.post_request anymore, but pre_request: we want the request to be blocked before it’s executed.
Alternatively, we could have blocked
CREATE queries by using input.sql_query.statement_type in another policy.Policy 4 - Blocking Access to Specific Native Users
You can control which users can access specific native users when they attempt to connect using the@<native_user> syntax. This is useful for enforcing least-privilege access and preventing users from accessing highly privileged accounts.
The following policy blocks the user john@joinformal.com from using the native user devops:
Block Access to Objects in AWS S3
This policy prevents access to S3 objects that were last modified more than 10 minutes ago. This is useful for ensuring data freshness and preventing access to outdated information.is_stalefunction: Compares the object’slast_modifiedtimestamp with the current time minus 10 minutes- Time calculation: Uses nanoseconds for precision (10 minutes = 600,000,000,000 nanoseconds)
- Blocking logic: Blocks
GetObjectrequests to objects inlocal-bucketthat are older than 10 minutes - Post-request stage: Uses
post_requestto block after the request is processed but before returning results
- Prevent access to outdated configuration files
- Ensure users only see recent data exports
- Block access to temporary files that should have been cleaned up
- Block multiple users from specific native users
- Allow only certain users to access privileged native users
- Require additional authentication for certain native users
Rate Limiting Access
Formal automatically tracks access patterns across different time windows and makes this data available to policies. You can use access count data to implement rate limiting policies that block users who exceed defined thresholds.Basic Rate Limiting by Minute
This policy blocks access tosensitive-bucket if the user has accessed it more than five times in the last minute:
Rate Limiting by Hour
You can also limit access over longer time windows. This policy blocks access if the user has made more than 100 requests in the last hour:- Automatic tracking: Formal automatically tracks access counts per user, per resource, per path (database/bucket name)
- Time windows: Access counts are available for minute (
data.access_count_minute) and hour (data.access_count_hour) windows - Cluster-wide: Access counts are synchronized across all Connector instances using distributed state
- Policy evaluation: Access count data is fetched and made available during policy evaluation at the
request,response, andsessionstages
data.access_count_minute[<bucket_name>]- Number of accesses in the last minutedata.access_count_hour[<bucket_name>]- Number of accesses in the last hour
- Prevent denial-of-service attacks by limiting request rates
- Enforce fair usage policies across users
- Protect against runaway scripts or applications
- Different rate limits for different buckets or databases
Rate limiting also works for databases. Use
data.access_count_minute[<database_name>] and data.access_count_hour[<database_name>] to limit database access patterns.Best Practices
As you can see with this example, policies can quickly get quite extensive. When creating policies, keep these best practices in mind:- Use the correct package name (
formal.v2for data policies). - Import required keywords (
future.keywords.if,future.keywords.in). - Use
session,request, andresponsestages accordingly. - Test policies before deploying to production by using the Dry-run status.
We recommend that you write and version your policies in git, and deploy them with Terraform using our provider.