Snowflake Native Application Security Model

Snowflake Native Apps enables developers to create data applications that leverage core Snowflake features, running entirely within Snowflake’s unified global platform. These applications can be distributed globally through the Snowflake Data Marketplace, reaching thousands of Snowflake customers across multiple regions. When a customer, referred to as a consumer, installs a Snowflake Native App, they retain complete control over the app’s access within their environment, ensuring all processing and data remain within their account. At the same time a Snowflake Native Application developer, known as a provider, can protect their intellectual property by keeping their code and logic hidden from the consumer. At the heart of what facilitates this interaction is the Native Application Security Model.

The Native Application Security Model is built on three fundamental principles:

Consumers Must Explicitly Give Access

Upon installation, a native application does not have default access to any objects in the account, including those objects that are granted to the PUBLIC role. The application can only interact with objects created by itself through the setup script or shared through the provider’s Application Package. Therefore, the consumer must explicitly grant the necessary permissions for the application to operate correctly. Providers can request these privileges by following the steps outlined in the official documentation.

Likewise, all external access for the application is blocked by default. This means the application cannot connect to the external world unless explicitly permitted by the consumer. External access is granted through EXTERNAL ACCESS INTEGRATIONS. Providers can request these integrations to ensure their applications function correctly by following the instructions provided in the official documentation.

Use Application Roles to Create the Visible Interface

A useful way to conceptualize a native application is as a black box. By default, the application cannot access anything outside itself, and conversely the consumer cannot see anything inside the application. Therefore, the application must also explicitly grant access to its objects for the consumer to interact with them. However, the application cannot create roles in the consumer’s account or know which roles need access to the application. Hence the existence of Application Roles. Application Roles are roles that are created within each application. When an application role is created, it is always granted to the consumer role used to install the application. Consequently, any objects granted to the application role become visible to the consumer, while everything else in the application remains hidden. Providers can create multiple application roles to offer different levels of access to consumers. The owner of the application can then further grant these application roles to other roles in their account to provider access for other users. Thus, application roles are used to expose the visible interface of the application.

Protect Provider Intellectual Property

Building on the previous point, an application needs to grant access to certain objects for the consumer to use them. However, providers may not want to expose all the details for the objects for worry of leaking implementation details or intellectual property. This is where Native Application IP Protection comes into play. When the provider grants the consumer access to certain features within the application, the consumer can use these features without being able to examine any of the underlying details. Even if the consumer runs SHOW/DESC commands, the sensitive fields within the application remain masked. Additionally one of the other features IP protection offers is masking the Query History. This means the queries issued by the application to Snowflake are masked from being inspected by the consumer. Therefore, this allows the application provider to only expose what is necessary whilst keeping the rest of the application and its underpinnings hidden from the consumer.

Under the Hood

You might be wondering, what exactly is the secret sauce behind the native application security model? The answer is simple: internal roles! When a native application is created, Snowflake generates hidden roles (highlighted in red) as part of the application. These roles are not visible to either the provider or consumer but they underpin the fundamentals of how an application works. Below is a diagram that highlights all these hidden roles that are created:

The internal roles and grant structures powering a Snowflake Native Application

APP_PRIMARY

This is the primary and most important hidden role, known as the APP_PRIMARY role. Created for each installation, this role allows the application to execute within the consumer’s account. Whilst the application is being installed or upgraded, the APP_PRIMARY role is used to execute the setup script. Therefore, objects such as stored procedures, Streamlit, or services created by the application run as the APP_PRIMARY role, when in owner’s right mode.

APP_IMPORTER

This role receives all privileges granted by the consumer to the application. Whenever consumers grant account-level privileges or privileges on individual objects, they are assigned to this role. Upon installation, the APP_IMPORTER role is automatically granted to the APP_PRIMARY role. This allows the “application” to gain access to the consumer’s objects. Therefore, when consumers grant privileges to an application under the hood it is granting the privilege to the APP_IMPORTER role.

SHARED_CONTENT

This role is the key to how sharing views and tables from the provider’s account works in native applications. It is created as part of the application package. When providers add shared data content to an application package they are actually granting privileges to this role. When the native application executes, it will have access to the privileges that are granted to the SHARED_CONTENT role. Since this role exists in the provider’s account to ensure security and prevent tampering, read-only privileges can be granted to the SHARED_CONTENT role.

Best Practices

Give only as much access as needed

As consumers, you might be tempted to grant the application more privileges than it needs. While this could simplify the setup process, not all privileges may strictly be necessary. For good security practices, only grant the privileges that the application explicitly requests. You can audit the privileges and references granted to the application through Snowsight or by running the command SHOW PRIVILEGES IN APPLICATION.

Conversely, as a provider, only grant to application roles the privileges required for the consumer to use the application. Avoid granting unnecessary privileges such as MONITOR or OPERATE unless they are essential, as these can give consumers additional control over your application.

Create multiple access levels for your applications

You can use application roles to create multiple “personas’’ for your application. The most common pattern is to create a “user” and “admin” persona. This can be done by creating two application roles app_user and app_admin (highlighted in green in the diagram). The app_user role can then be granted to the app_admin role. From there on the admin role can contain more privileges required to setup and configure the application whereas the user role can only be used to run the application. You can also customize your UI experience by creating different Streamlit applications and granting them to each role. For example, you may want to create a “Settings’’ Streamlit application that’s only visible to the app_admin. Or, if you are using Snowpark Container Services(PuPr), then you can grant the the specific service role to the app_admin role.

You can take this one step further and even create data and masking policies. For example, you can create a masking policy using the function is_application_role_in_session(string)

create or replace masking policy app_schema.admin_policy as (val string) returns string ->
case
when is_application_role_in_session('app_admin') then val
else '****'
end;

and apply it to a table or view using the ALTER TABLE or ALTER VIEW command. If this masking policy is applied to a table or view and the active role is granted the app_admin application role, then only they will have access to view the data. Otherwise, the data will be masked out as ****. The application code (stored procedures, Streamlits, services, etc) owned by APP_PRIMARY however will always be able to see all the data.

As consumers of the application this also offers better administrative control over the application. Since the owner of the application has access to all application roles they are automatically the admin. However, other roles which require access to the application (such as CONSUMER_ROLE highlighted in blue in the diagram) can be granted the app_user application role. On the other hand if more roles require admin access the app_admin application role can be further granted by the consumer. This allows everyone to use the application but allows it to be administered by only a few individuals.

Getting Started

Interested in getting started ? Head over to the Native Apps Examples to get up and running.

--

--