---
title: Security Configurations
sidebar_position: 1
---

Authentication and authorization in Superset is handled by Flask AppBuilder (FAB), an application development framework
built on top of Flask. FAB provides authentication, user management, permissions and roles.
Please read its [Security documentation](https://flask-appbuilder.readthedocs.io/en/latest/security.html).

### Provided Roles

Superset ships with a set of roles that are handled by Superset itself. You can assume
that these roles will stay up-to-date as Superset evolves (and as you update Superset versions).

Even though **Admin** users have the ability, we don't recommend altering the
permissions associated with each role (e.g. by removing or adding permissions to them). The permissions
associated with each role will be re-synchronized to their original values when you run
the **superset init** command (often done between Superset versions).

A table with the permissions for these roles can be found at [/RESOURCES/STANDARD_ROLES.md](https://github.com/apache/superset/blob/master/RESOURCES/STANDARD_ROLES.md).

### Admin

Admins have all possible rights, including granting or revoking rights from other
users and altering other people’s slices and dashboards.

>#### Threat Model and Privilege Boundaries: The Admin Role
>
>Apache Superset is built with a granular permission model where users assigned the Admin role are considered fully trusted. Admins possess complete control over the application's configuration, UI rendering, and access controls.
>
>Consequently, actions performed by an Admin that alter the application's behavior or presentation—such as injecting custom CSS, modifying Jinja templates, or altering security flags—are intended administrative capabilities by design.
>
>In accordance with MITRE CNA Rule 4.1, a vulnerability must represent a violation of an explicit security policy. Because the Admin role is defined as a trusted operational boundary, actions executed with Admin privileges do not cross a security perimeter. Therefore, exploit vectors that strictly require Admin access are not classified as security vulnerabilities and are ineligible for CVE assignment.

### Alpha

Alpha users have access to all data sources, but they cannot grant or revoke access
from other users. They are also limited to altering the objects that they own. Alpha users can add and alter data sources.

### Gamma

Gamma users have limited access. They can only consume data coming from data sources
they have been given access to through another complementary role. They only have access to
view the slices and dashboards made from data sources that they have access to. Currently Gamma
users are not able to alter or add data sources. We assume that they are mostly content consumers, though they can create slices and dashboards.

Also note that when Gamma users look at the dashboards and slices list view, they will
only see the objects that they have access to.

### sql_lab

The **sql_lab** role grants access to SQL Lab. Note that while **Admin** users have access
to all databases by default, both **Alpha** and **Gamma** users need to be given access on a per database basis.

Beyond the base `sql_lab` role, two additional SQL Lab permissions must be explicitly granted for users who need these capabilities:

| Permission | Feature |
|------------|---------|
| `can_estimate_query_cost` on `SQLLab` | Estimate query cost before running |
| `can_format_sql` on `SQLLab` | Format SQL using the database's dialect |

Grant these in **Security → List Roles** by adding the permissions to the relevant role.

### Public

The **Public** role is the most restrictive built-in role, designed specifically for anonymous/unauthenticated
users who need to view dashboards. It provides minimal read-only access for:

- Viewing dashboards and charts
- Using interactive dashboard filters
- Accessing dashboard and chart permalinks
- Reading embedded dashboards
- Viewing annotations on charts

The Public role explicitly excludes:
- Any write permissions on dashboards, charts, or datasets
- SQL Lab access
- Share functionality
- User profile or admin features
- Menu access to most Superset features

Anonymous users are automatically assigned the Public role when `AUTH_ROLE_PUBLIC` is configured
(a Flask-AppBuilder setting). The `PUBLIC_ROLE_LIKE` setting is **optional** and controls what
permissions are synced to the Public role when you run `superset init`:

```python
# Optional: Sync sensible default permissions to the Public role
PUBLIC_ROLE_LIKE = "Public"

# Alternative: Copy permissions from Gamma for broader access
# PUBLIC_ROLE_LIKE = "Gamma"
```

If you prefer to manually configure the Public role's permissions, or use subject-based
dashboard viewer assignments, you do not need to set `PUBLIC_ROLE_LIKE`.

**Important notes:**

- **Data access is still required by default:** The Public role only grants UI/API permissions.
  You must also grant access to specific datasets necessary to view a dashboard. As with other
  roles, this can be done in two ways:

  - **Dataset-based access:** Dashboards only appear in the list and are accessible if the user
    has permission to at least one of their datasets. Grant dataset access by editing the Public
    role in the Superset UI (Menu -> Security -> List Roles -> Public) and adding the relevant
    data sources. All published dashboards using those datasets become visible.

  - **Subject-based dashboard access:** When `ENABLE_VIEWERS` is enabled, anonymous users can
    see published dashboards where the "Public" role subject has been explicitly added as a
    viewer. If `VIEWER_PROMISCUOUS_MODE` is enabled, viewer access can bypass dataset
    permission checks for dashboard rendering.

- **Role synchronization:** Built-in role permissions (Admin, Alpha, Gamma, sql_lab, and Public
  when `PUBLIC_ROLE_LIKE = "Public"`) are synchronized when you run `superset init`. Any manual
  permission edits to these roles may be overwritten during upgrades. To customize the Public
  role permissions, you can either:
  - Edit the Public role directly and avoid setting `PUBLIC_ROLE_LIKE` (permissions won't be
    overwritten by `superset init`)
  - Copy the Public role via "Copy Role" in the Superset web UI, save it under a different name
    (e.g., "Public_Custom"), customize the permissions, then update **both** configs:
    `PUBLIC_ROLE_LIKE = "Public_Custom"` and `AUTH_ROLE_PUBLIC = "Public_Custom"`

### Managing Data Source Access for Gamma Roles

Here’s how to provide users access to only specific datasets. First make sure the users with
limited access have [only] the Gamma role assigned to them. Second, create a new role (Menu -> Security -> List Roles) and click the + sign.

This new window allows you to give this new role a name, attribute it to users and select the
tables in the **Permissions** dropdown. To select the data sources you want to associate with this role, simply click on the dropdown and use the typeahead to search for your table names.

You can then confirm with users assigned to the **Gamma** role that they see the
objects (dashboards and slices) associated with the tables you just extended them.

### Subjects

A **subject** is a unified identity that can be granted access to Superset resources such as
dashboards, charts, and datasets. Subjects come in three types:

- **User** — an individual Superset user
- **Group** — a FAB group
- **Role** — a FAB role (e.g. Alpha, Gamma, or any custom role)

Subjects are used throughout Superset to assign **editors** (who can modify a resource) and
**viewers** (who can view it). For new resource-specific assignments, Superset defaults to
**Users** and **Groups** in subject pickers. Groups are the recommended way to grant access to a
set of people because they represent organizational membership, while roles remain focused on
capability grants such as "can read dashboards" or "can write charts".

Role subjects are still supported for backwards compatibility with features that previously used
roles directly, including Row Level Security role assignments and `DASHBOARD_RBAC`. Existing role
subject assignments continue to be enforced even when roles are not exposed in the default picker
configuration. However, when an administrator edits a subject list, roles are not available as new
dropdown values unless that picker has been configured to expose role subjects. Roles are not
recommended for new subject assignments, because using roles for both permissions and resource
membership couples two separate concerns.

Administrators can control which subject types are available in pickers with
`SUBJECTS_RELATED_TYPES`. By default, this setting exposes only users and groups:

```python
from superset.subjects.types import SubjectType

SUBJECTS_RELATED_TYPES = [
    SubjectType.USER,
    SubjectType.GROUP,
]
```

Set `SUBJECTS_RELATED_TYPES = None` to expose all subject types, including roles. To expose roles
only for a specific entity, set that entity's override:

```python
from superset.subjects.types import SubjectType

SUBJECTS_RELATED_TYPES = [
    SubjectType.USER,
    SubjectType.GROUP,
]

# Leave dashboard, chart, and alert/report overrides unset so they inherit
# the users + groups default. Expose roles only in the RLS subject picker for
# compatibility with existing role-based RLS workflows.
SUBJECTS_RELATED_TYPES_RLS = [
    SubjectType.USER,
    SubjectType.GROUP,
    SubjectType.ROLE,
]
```

Available per-entity overrides are:

- `SUBJECTS_RELATED_TYPES_DASHBOARDS` for dashboard editor/viewer pickers
- `SUBJECTS_RELATED_TYPES_CHARTS` for chart editor/viewer pickers
- `SUBJECTS_RELATED_TYPES_RLS` for Row Level Security subject pickers
- `SUBJECTS_RELATED_TYPES_ALERT_REPORTS` for alert/report editor pickers

When an override is set, it replaces `SUBJECTS_RELATED_TYPES` for that picker. When it is `None`,
the picker inherits the global default.

### Dashboard Access Control

Access to dashboards is managed via editors (subjects that have edit permissions to the dashboard).
Non-editor user access can be managed in two ways. Note that dashboards must be published to be
visible to other users.

#### Dataset-Based Access (Default)

By default, users can view published dashboards if they have access to at least one dataset
used in that dashboard. Grant dataset access by adding the relevant data source permissions
to a role (Menu → Security → List Roles).

This is the simplest approach but provides all-or-nothing access based on dataset permissions—
if a user has access to a dataset, they can see all published dashboards using that dataset.

#### Dashboard and Chart-Level Access

For fine-grained control over which dashboards and charts specific users or groups can access,
enable subject-based viewers:

```python
FEATURE_FLAGS = {
    "ENABLE_VIEWERS": True,
}
```

With this enabled, you can assign viewer subjects to each dashboard or chart in its properties.
Users will see resources where they are assigned directly, or indirectly through a group subject.
Role subjects can also be enabled for compatibility, but groups are recommended for new viewer
assignments.

`ENABLE_VIEWERS` can be adopted incrementally. Dashboards and charts with no assigned viewers keep
the existing implicit dataset-access behavior: users who can access the underlying dataset can still
see published dashboards that use that dataset and charts backed by that dataset. Assigning one or
more viewers changes that specific resource to explicit viewer access for non-editors. Removing all
viewers returns the resource to the dataset-based fallback. Explicit Viewers are the preferred model
going forward; the implicit fallback may be deprecated and removed in a later major version.

**Important considerations:**
- Viewer access uses normal dataset checks unless `VIEWER_PROMISCUOUS_MODE` is enabled
- With `VIEWER_PROMISCUOUS_MODE`, dashboard viewer access can bypass dataset-level checks for
  charts and datasets in that dashboard
- Dashboards and charts without assigned viewers fall back to dataset-based access
- The dashboard must still be published to be visible

This feature is particularly useful for:
- Making specific dashboards public while keeping others private
- Granting access to dashboards without exposing the underlying datasets for other uses
- Creating dashboard-specific access patterns that don't align with dataset permissions

### SQL Execution Security Considerations

Apache Superset includes features designed to provide safeguards when interacting with connected databases, such as the `DISALLOWED_SQL_FUNCTIONS` configuration setting. This aims to prevent the execution of potentially harmful database functions or system variables directly from Superset interfaces like SQL Lab.

However, it is crucial to understand the following:

**Superset is Not a Database Firewall**: Superset's built-in checks, like `DISALLOWED_SQL_FUNCTIONS`, provide a layer of protection but cannot guarantee complete security against all database-level threats or advanced bypass techniques (like specific comment injection methods). They should be viewed as a supplement to, not a replacement for, robust database security.

**Configuration is Key**: The effectiveness of Superset's safeguards heavily depends on proper configuration by the Superset administrator. This includes maintaining the `DISALLOWED_SQL_FUNCTIONS` list, carefully managing feature flags (like `ENABLE_TEMPLATE_PROCESSING`), and configuring other security settings appropriately.

**Database Security is Paramount**: The ultimate responsibility for securing database access, controlling permissions, and preventing unauthorized function execution lies with the database administrators (DBAs) and security teams managing the underlying database instance.

**Recommended Database Practices**: We strongly recommend implementing security best practices at the database level, including:
* **Least Privilege**: Connecting Superset using dedicated database user accounts with the minimum permissions required for Superset's operation (typically read-only access to necessary schemas/tables).
* **Database Roles & Permissions**: Utilizing database-native roles and permissions to restrict access to sensitive functions, system variables (like `@@hostname`), schemas, or tables.
* **Network Security**: Employing network-level controls like database firewalls or proxies to restrict connections.
* **Auditing**: Enabling database-level auditing to monitor executed queries and access patterns.

By combining Superset's configurable safeguards with strong database-level security practices, you can achieve a more robust and layered security posture.

**Dataset Sample Access**: The `get_samples()` endpoint now enforces datasource-level access control. Users can only fetch sample rows from datasets they have been explicitly granted access to — the same permission check applied when running chart queries. This closes a prior gap where unauthenticated or under-privileged access could retrieve sample data.

### REST API for user & role management

Flask-AppBuilder supports a REST API for user CRUD,
but this feature is in beta and is not enabled by default in Superset.
To enable this feature, set the following in your Superset configuration:

```python
FAB_ADD_SECURITY_API = True
```

Once configured, the documentation for additional "Security" endpoints will be visible in Swagger for you to explore.

### API Key Authentication

Superset supports long-lived API keys for service accounts, CI/CD pipelines, and programmatic integrations (including MCP clients).

#### Enabling API Key Authentication

API key authentication is **disabled by default**. To turn it on, set the Flask-AppBuilder config value in `superset_config.py` and also enable the matching feature flag so the management UI is exposed:

```python
FAB_API_KEY_ENABLED = True

FEATURE_FLAGS = {
    "FAB_API_KEY_ENABLED": True,
}
```

The config value registers the `ApiKeyApi` blueprint on the backend; the feature flag controls whether the UI for managing keys appears for the user. See the [Feature Flags](/admin-docs/configuration/feature-flags) documentation for more on feature flag configuration.

#### Creating an API Key

Once enabled, each user manages their own keys from their profile page:

1. Open the user menu (top-right) and click **Info** to navigate to the User Info page
2. Expand the **API Keys** section
3. Click **+ API Key**
4. Enter a name and (optionally) an expiration date
5. Copy the generated token — it is shown only once

Only users with the `can_read` and `can_write` permissions on `ApiKey` (granted by default to Admins) can manage API keys.

#### Using an API Key

Pass the key as a Bearer token in the `Authorization` header:

```
Authorization: Bearer <your-api-key>
```

This works for all REST API endpoints and the MCP server. The request is executed with the permissions of the user who created the key.

#### Use Cases

- **CI/CD pipelines** — automated chart/dashboard exports and imports
- **MCP integrations** — connect AI assistants without interactive login
- **External services** — dashboards embedded in other applications
- **Service accounts** — long-lived credentials that don't expire with session cookies

:::caution
Store API keys securely. Anyone with a valid key can make requests on behalf of the creating user. Revoke keys promptly if they are compromised by deleting them from the **API Keys** section of your User Info page.
:::

### Customizing Permissions

The permissions exposed by FAB are very granular and allow for a great level of
customization. FAB creates many permissions automatically for each model that is
created (can_add, can_delete, can_show, can_edit, …) as well as for each view.
On top of that, Superset can expose more granular permissions like **all_datasource_access**.

**We do not recommend altering the 3 base roles as there are a set of assumptions that
Superset is built upon**. It is possible though for you to create your own roles, and union them to existing ones.

### Permissions

Roles are composed of a set of permissions, and Superset has many categories of
permissions. Here are the different categories of permissions:

- Model & Action: models are entities like Dashboard, Slice, or User. Each model has
  a fixed set of permissions, like **can_edit**, **can_show**, **can_delete**, **can_list**, **can_add**,
  and so on. For example, you can allow a user to delete dashboards by adding **can_delete** on
  Dashboard entity to a role and granting this user that role.
- Views: views are individual web pages, like the Explore view or the SQL Lab view.
  When granted to a user, they will see that view in its menu items, and be able to load that page.
- Data source: For each data source, a permission is created. If the user does not have the
  `all_datasource_access permission` granted, the user will only be able to see Slices or explore the data sources that are granted to them
- Database: Granting access to a database allows for the user to access all
  data sources within that database, and will enable the user to query that
  database in SQL Lab, provided that the SQL Lab specific permission have been granted to the user

### Restricting Access to a Subset of Data Sources

We recommend giving a user the **Gamma** role plus any other roles that would add
access to specific data sources. We recommend that you create individual roles for
each access profile. For example, the users on the Finance team might have access to a set of
databases and data sources; these permissions can be consolidated in a single role.
Users with this profile then need to be assigned the **Gamma** role as a foundation to
the models and views they can access, and that Finance role that is a collection of permissions to data objects.

A user can have multiple roles associated with them. For example, an executive on the Finance
team could be granted **Gamma**, **Finance**, and the **Executive** roles. The **Executive**
role could provide access to a set of data sources and dashboards made available only to executives.
In the **Dashboards** view, a user can only see the ones they have access to
based on the roles and permissions that were attributed.

### Row Level Security

Using Row Level Security filters (under the **Security** menu) you can create filters
that are assigned to a particular dataset, as well as a set of subjects.
If you want members of the Finance team to only have access to
rows where `department = "finance"`, you could:

- Create a Row Level Security filter with that clause (`department = "finance"`)
- Then assign the clause to the **Finance** role subject and the dataset it applies to

The **clause** field, which can contain arbitrary text, is then added to the generated
SQL statement's WHERE clause. So you could even do something like create a filter
for the last 30 days and apply it to a specific subject, with a clause
like `date_field > DATE_SUB(NOW(), INTERVAL 30 DAY)`. It can also support
multiple conditions: `client_id = 6` AND `advertiser="foo"`, etc.

RLS clauses also support **Jinja templating** when `ENABLE_TEMPLATE_PROCESSING` is enabled, so you can write dynamic filters such as
`user_id = '{{ current_username() }}'` to restrict rows based on the logged-in user.

#### Filter Types

There are two types of RLS filters:

- **Regular** — The filter clause is applied when the querying user matches one of the
  subjects assigned to the filter. Use this to restrict what specific users, roles, or groups
  can see.
- **Base** — The filter clause is applied to **all** users _except_ those matching the assigned
  subjects. Use this to define a default restriction that privileged subjects (e.g. the Admin
  role subject) are exempt from. For example, a Base filter with clause `1 = 0` and the Admin
  role subject would hide all rows from everyone except Admin - useful as a deny-by-default
  baseline.

#### Group Keys and Filter Combination

All applicable RLS filters are combined before being added to the query. The combination
rules are:

- Filters that share the **same group key** are combined with **OR** (any match within
  the group is sufficient).
- Different filter groups (different group keys, or no group key) are combined with
  **AND** (all groups must match).
- Filters with **no group key** are each treated as their own group and are always AND'd.

For example, if a dataset has three filters:

| Filter | Clause | Group Key |
|--------|--------|-----------|
| F1 | `department = 'Finance'` | `department` |
| F2 | `department = 'Marketing'` | `department` |
| F3 | `region = 'Europe'` | `region` |

The resulting WHERE clause would be:

```sql
(department = 'Finance' OR department = 'Marketing') AND (region = 'Europe')
```

:::caution Conflicting filters
It is possible to create filters that conflict and produce an empty result set. For
example, the filters `client_id = 4` and `client_id = 5` **without a shared group key**
will be AND'd together, producing `client_id = 4 AND client_id = 5`, which can never
be true.

If you intend for these to be alternatives, assign them the **same group key** so they
are OR'd instead.
:::

#### RLS and Virtual (SQL-Based) Datasets

RLS filters are assigned to **datasets**, not to underlying database tables directly. This
has important implications when working with virtual (SQL-based) datasets:

- **Physical datasets** (backed directly by a table or view) — RLS filters assigned to
  the dataset are added as WHERE clauses to the query.
- **Virtual datasets** (defined by a custom SQL query) — RLS filters assigned directly to
  the virtual dataset are applied to the _outer_ query that wraps the dataset's SQL.
  Additionally, RLS filters on the **underlying physical datasets** referenced by the
  virtual dataset's SQL are injected into the inner subquery for each referenced table.

For example, if you have:

1. A physical dataset `orders` with RLS filter `region = 'US'`
2. A virtual dataset defined as `SELECT * FROM orders WHERE status = 'active'`

A user affected by the RLS filter will effectively see:

```sql
SELECT * FROM (
  SELECT * FROM orders WHERE (region = 'US') AND status = 'active'
) ...
```

**Key considerations for virtual datasets:**

- You generally do **not** need to duplicate RLS filters on both the physical and virtual
  dataset — filters on the physical dataset are applied automatically at query time.
- If you assign an RLS filter directly to a virtual dataset, the clause must reference
  columns available in the virtual dataset's _output_, not necessarily the underlying
  table's columns.
- In **SQL Lab**, RLS is enforced only when the `RLS_IN_SQLLAB` feature flag is enabled:
  queries run against tables that have associated datasets with RLS filters will then have
  the appropriate predicates injected automatically.

#### Checking RLS Filters via the API

You can use the RLS REST API to audit which filters are configured and which datasets
they affect. This requires the `can_read` permission on the `Row Level Security` resource.

**List all RLS rules:**

```
GET /api/v1/rowlevelsecurity/
```

**Filter RLS rules for a specific dataset** (using [Rison](https://github.com/Nanonid/rison) query syntax):

```
GET /api/v1/rowlevelsecurity/?q=(filters:!((col:tables,opr:rel_m_m,value:<dataset_id>)))
```

**Filter RLS rules by subject:**

```
GET /api/v1/rowlevelsecurity/?q=(filters:!((col:subjects,opr:rel_m_m,value:<subject_id>)))
```

**View details of a specific rule** (including clause, assigned datasets, and subjects):

```
GET /api/v1/rowlevelsecurity/<id>
```

The response includes the filter's `name`, `filter_type` (Regular or Base), `clause`,
`group_key`, assigned `tables` (with id, schema, and table\_name), and assigned `subjects`.

:::tip Auditing RLS for virtual datasets
To find all RLS rules that could affect a particular virtual dataset, query the list
endpoint filtered by that dataset's ID for any directly-assigned rules. Then also check
the physical datasets referenced in the virtual dataset's SQL, since their RLS filters
are applied at query time too.
:::

### User Sessions

Superset uses [Flask](https://pypi.org/project/Flask/)
and [Flask-Login](https://pypi.org/project/Flask-Login/) for user session management.

Session cookies are used to maintain session info and user state between requests,
although they do not contain personal user information they serve the purpose of identifying
a user session on the server side.
The session cookie is encrypted with the application `SECRET_KEY` and cannot be read by the client.
So it's very important to keep the `SECRET_KEY` secret and set to a secure unique complex random value.

Flask and Flask-Login offer a number of configuration options to control session behavior.

- Relevant Flask settings:

`SESSION_COOKIE_HTTPONLY`: (default: `False`): Controls if cookies should be set with the `HttpOnly` flag.

`SESSION_COOKIE_SECURE`: (default: `False`) Browsers will only send cookies with requests over
HTTPS if the cookie is marked “secure”. The application must be served over HTTPS for this to make sense.

`SESSION_COOKIE_SAMESITE`: (default: "Lax") Prevents the browser from sending this cookie along with cross-site requests.

`PERMANENT_SESSION_LIFETIME`: (default: "31 days") The lifetime of a permanent session as a `datetime.timedelta` object.

#### Switching to server side sessions

Server side sessions offer benefits over client side sessions on security and performance.
By enabling server side sessions, the session data is stored server side and only a session ID
is sent to the client. When a user logs in, a session is created server side and the session ID
is sent to the client in a cookie. The client will send the session ID with each request and the
server will use it to retrieve the session data.
On logout, the session is destroyed server side and the session cookie is deleted on the client side.
This reduces the risk for replay attacks and session hijacking.

Superset uses [Flask-Session](https://flask-session.readthedocs.io/en/latest/) to manage server side sessions.
To enable this extension you have to set:

``` python
SESSION_SERVER_SIDE = True
```

Flask-Session offers multiple backend session interfaces for Flask, here's an example for Redis:

``` python
from redis import Redis

SESSION_TYPE = "redis"
SESSION_REDIS = Redis(host="redis", port=6379, db=0)
# sign the session cookie sid
SESSION_USE_SIGNER = True
```

### Content Security Policy (CSP)

Superset uses the [Talisman](https://pypi.org/project/flask-talisman/) extension to enable implementation of a
[Content Security Policy (CSP)](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP), an added
layer of security that helps to detect and mitigate certain types of attacks, including
Cross-Site Scripting (XSS) and data injection attacks.

A CSP makes it possible for server administrators to reduce or eliminate the vectors by which XSS can
occur by specifying the domains that the browser should consider to be valid sources of executable scripts.
A CSP-compatible browser will then only execute scripts loaded in source files received from those allowed domains,
ignoring all other scripts (including inline scripts and event-handling HTML attributes).

A policy is described using a series of policy directives, each of which describes the policy for
a certain resource type or policy area. You can check possible directives
[here](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy).

It's extremely important to correctly configure a Content Security Policy when deploying Superset to
prevent many types of attacks. Superset provides two variables in `config.py` for deploying a CSP:

- `TALISMAN_ENABLED` defaults to `True`; set this to `False` in order to disable CSP
- `TALISMAN_CONFIG` holds the actual the policy definition (*see example below*) as well as any
other arguments to be passed to Talisman.

When running in production mode, Superset will check at startup for the presence
of a CSP. If one is not found, it will issue a warning with the security risks. For environments
where CSP policies are defined outside of Superset using other software, administrators can disable
this warning using the `CONTENT_SECURITY_POLICY_WARNING` key in `config.py`.

#### CSP Requirements

- Superset needs the `style-src unsafe-inline` CSP directive in order to operate.

  ```
  style-src 'self' 'unsafe-inline'
  ```

- Only scripts marked with a [nonce](https://content-security-policy.com/nonce/) can be loaded and executed.
Nonce is a random string automatically generated by Talisman on each page load.
You can get current nonce value by calling jinja macro `csp_nonce()`.

  ```html
  <script nonce="{{ csp_nonce() }}">
  /* my script */
  </script>
  ```

- Some dashboards load images using data URIs and require `data:` in their `img-src`

  ```
  img-src 'self' data:
  ```

- MapBox charts use workers and need to connect to MapBox servers in addition to the Superset origin

  ```
  worker-src 'self' blob:
  connect-src 'self' https://api.mapbox.com https://events.mapbox.com
  ```

- Cartodiagram charts request map data (image and json) from external resources that can be edited by users,
and therefore either require a list of allowed domains to request from or a wildcard (`'*'`) for `img-src` and `connect-src`.

- Other CSP directives default to `'self'` to limit content to the same origin as the Superset server.

In order to adjust provided CSP configuration to your needs, follow the instructions and examples provided in
[Content Security Policy Reference](https://content-security-policy.com/)

#### Other Talisman security considerations

Setting `TALISMAN_ENABLED = True` will invoke Talisman's protection with its default arguments,
of which `content_security_policy` is only one. Those can be found in the
[Talisman documentation](https://pypi.org/project/flask-talisman/) under *Options*.
These generally improve security, but administrators should be aware of their existence.

In particular, the option of `force_https = True` (`False` by default) may break Superset's Alerts & Reports
if workers are configured to access charts via a `WEBDRIVER_BASEURL` beginning
with `http://`.  As long as a Superset deployment enforces https upstream, e.g.,
through a load balancer or application gateway, it should be acceptable to keep this
option disabled. Otherwise, you may want to enable `force_https` like this:

```python
TALISMAN_CONFIG = {
    "force_https": True,
    "content_security_policy": { ...
```

#### Configuring Talisman in Superset

Talisman settings in Superset can be modified using superset_config.py. If you need to adjust security policies, you can override the default configuration.

Example: Overriding Talisman Configuration in superset_config.py for loading images form s3 or other external sources.

```python
TALISMAN_CONFIG = {
    "content_security_policy": {
        "base-uri": ["'self'"],
        "default-src": ["'self'"],
        "img-src": [
            "'self'",
            "blob:",
            "data:",
            "https://apachesuperset.gateway.scarf.sh",
            "https://static.scarf.sh/",
            # "https://cdn.brandfolder.io", # Uncomment when SLACK_ENABLE_AVATARS is True  # noqa: E501
            "ows.terrestris.de",
            "aws.s3.com", # Add Your Bucket or external data source
        ],
        "worker-src": ["'self'", "blob:"],
        "connect-src": [
            "'self'",
            "https://api.mapbox.com",
            "https://events.mapbox.com",
        ],
        "object-src": "'none'",
        "style-src": [
            "'self'",
            "'unsafe-inline'",
        ],
        "script-src": ["'self'", "'strict-dynamic'"],
    },
    "content_security_policy_nonce_in": ["script-src"],
    "force_https": False,
    "session_cookie_secure": False,
}
```

For more information on setting up Talisman, please refer to
https://superset.apache.org/admin-docs/configuration/networking-settings/#changing-flask-talisman-csp.

### Reporting Security Vulnerabilities

Apache Software Foundation takes a rigorous standpoint in annihilating the security issues in its
software projects. Apache Superset is highly sensitive and forthcoming to issues pertaining to its
features and functionality.

If you have apprehensions regarding Superset security or you discover vulnerability or potential
threat, don’t hesitate to get in touch with the Apache Security Team by dropping a mail at
security@apache.org. In the mail, specify the project name Superset with the description of the
issue or potential threat. You are also urged to recommend the way to reproduce and replicate the
issue. The security team and the Superset community will get back to you after assessing and
analysing the findings.

PLEASE PAY ATTENTION to report the security issue on the security email before disclosing it on
public domain. The ASF Security Team maintains a page with the description of how vulnerabilities
and potential threats are handled, check [their web page](https://apache.org/security/committers.html)
for more details.
