---
title: Security
description: Learn how to protect your website from CSRF attacks using the `sameSite` cookie attribute.
sidebar:
  order: 70
---

## Overview

The following page takes you through some common security considerations that the **SuperTokens** `Session` recipe handles.

---

## Anti-csrf

CSRF attacks can happen if a logged in user visits a malicious website which makes an API call to your website's API to maliciously change that user's data.
To protect against this attack, the cookie `sameSite` attribute works along with some anti-csrf measures.
This attribute declares if your cookies should restrict to a first-party or same-site context.
Configuring `sameSite` can prevent CSRF attacks.

For example, if `sameSite` is `lax`, the browser only sends cookies for requests that originate from the same top level domain as the API's domain.
If a user visits a malicious site, requests from those sites do not have the session cookies.

### Configure anti-csrf

:::warning[- SuperTokens automatically defends against CSRF attacks.]
- Please only change this setting if you know what you are doing. If you are unsure, please feel free to [ask questions](https://supertokens.com/discord).
- This setting does not apply while using header-based authentication, since they get the same protection as `antiCsrf` set to `VIA_CUSTOM_HEADER`.
:::

You can change the `antiCsrf` configuration option to take control of the kind of protection you get.
You can use on of the following values:

- `"NONE"` would disable any anti-csrf protection from our end.
You can use this if you have an implementation of CSRF protection.
- `"VIA_CUSTOM_HEADER"` uses [this method](https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#use-of-custom-request-headers) to prevent CSRF protection.
This sets automatically if `sameSite` is `none` or if your `apiDomain` and `websiteDomain` do not share the same top level domain name.
- `"VIA_TOKEN"` uses an explicit anti-csrf token.
Use this method if you want to allow any origin to query your APIs.
This method may cause issues in browsers like Safari, especially if your site embeds as an `iframe`.

<CodeGroup group="backend-language">
<Tab title="Node.js" value="nodejs">
```tsx
import SuperTokens from "supertokens-node";
import Session from "supertokens-node/recipe/session";

SuperTokens.init({
  supertokens: {
    connectionURI: "...",
  },
  appInfo: {
    apiDomain: "...",
    appName: "...",
    websiteDomain: "...",
  },
  recipeList: [
    Session.init({
      antiCsrf: "VIA_CUSTOM_HEADER", // Should be one of "NONE" or "VIA_CUSTOM_HEADER" or "VIA_TOKEN"
    }),
  ],
});
```
</Tab>
<Tab title="Go" value="go">
```go
import (
	"github.com/supertokens/supertokens-golang/recipe/session"
	"github.com/supertokens/supertokens-golang/recipe/session/sessmodels"
	"github.com/supertokens/supertokens-golang/supertokens"
)

func main() {
	// Should be one of "NONE" or "VIA_CUSTOM_HEADER" or "VIA_TOKEN"
	antiCsrf := "VIA_CUSTOM_HEADER"

	supertokens.Init(supertokens.TypeInput{
		RecipeList: []supertokens.Recipe{
			session.Init(&sessmodels.TypeInput{
				AntiCsrf: &antiCsrf,
			}),
		},
	})
}
```
</Tab>
<Tab title="Python" value="python">
```python check=false reason="Partial configuration example"
from supertokens_python import init, InputAppInfo
from supertokens_python.recipe import session

init(
    app_info=InputAppInfo(api_domain="...", app_name="...", website_domain="..."),
    framework='...',
    recipe_list=[
        session.init(
            # Should be one of "NONE" or "VIA_CUSTOM_HEADER" or "VIA_TOKEN"
            anti_csrf='VIA_CUSTOM_HEADER'
        )
    ]
)
```
</Tab>
</CodeGroup>

---

## Cookie consent

[Per GDPR](https://gdpr.eu/cookies/), users do not need to give consent for your application to use session cookies. This is because they fall under essential cookies and not tracking cookies:

:::info[Important]
"While it is not required to obtain consent for these cookies, explain to the user what they do and why they are necessary."
:::

---

## Same site cookies

To ensure session cookies have protection from CSRF attacks, the ``sameSite`` cookie attribute ensures this protection.
The ``sameSite`` cookie attribute declares if your cookies should restrict to a first-party or same-site context.
The ``sameSite`` attribute can have three possible values:
- ``none``
  - Cookies attach in all contexts, that is, cookies attach to both first-party and cross-origin requests.
  - On Safari however, if third-party cookies do not work (which is the default behavior), and the website and `API` domains do not share the same top-level domain, then cookies do not go. Please check [the session management page](/post-authentication/session-management/switch-between-cookies-and-header-authentication) to see how you can switch to using headers.
- ``lax``
  - Cookies are only sent in a first-party context and along with `GET` requests initiated by third party websites (that result in browser navigation - user clicking on a link).
- ``strict``
  - Cookies are only sent in a first-party context and not sent along with requests initiated by third party websites.

### Configuration

:::warning[- SuperTokens automatically sets the value of the ``sameSite`` cookie attribute based on your website and `API` domain configuration.]
- Please only change this setting if you are a web security expert. If you are unsure, please feel free to [ask questions](https://supertokens.com/discord).
:::

<CodeGroup group="backend-language">
<Tab title="Node.js" value="nodejs">
```tsx
import SuperTokens from "supertokens-node";
import Session from "supertokens-node/recipe/session";

SuperTokens.init({
  supertokens: {
    connectionURI: "...",
  },
  appInfo: {
    apiDomain: "...",
    appName: "...",
    websiteDomain: "...",
  },
  recipeList: [
    Session.init({
      cookieSameSite: "strict", // Should be one of "strict" or "lax" or "none"
    }),
  ],
});
```
</Tab>
<Tab title="Go" value="go">
```go
import (
	"github.com/supertokens/supertokens-golang/recipe/session"
	"github.com/supertokens/supertokens-golang/recipe/session/sessmodels"
	"github.com/supertokens/supertokens-golang/supertokens"
)

func main() {
	// Should be one of "strict" or "lax" or "none"
	cookieSameSite := "lax"

	supertokens.Init(supertokens.TypeInput{
		RecipeList: []supertokens.Recipe{
			session.Init(&sessmodels.TypeInput{
				CookieSameSite: &cookieSameSite,
			}),
		},
	})
}
```
</Tab>
<Tab title="Python" value="python">
```python check=false reason="Partial configuration example"
from supertokens_python import init, InputAppInfo
from supertokens_python.recipe import session


init(
    app_info=InputAppInfo(api_domain="...", app_name="...", website_domain="..."),
    framework='...',
    recipe_list=[
        session.init(
            cookie_same_site='lax' # Should be one of 'strict' or 'lax' or 'none'
        )
    ]
)
```
</Tab>
</CodeGroup>

---

## Cookies and HTTPS

SuperTokens ensures that cookies have security by enabling the ``secure`` flag when generating session cookies.
When set, the ``secure`` attribute limits the scope of the cookie to attach only to secure domains.
This results in the cookie only attaching to requests transmitted over `https`.
This, in turn, prevents cookie theft via man in the middle attacks.

You can explicitly set the security level of cookies using the next snippet:

:::note[If not explicitly set, SuperTokens automatically determines the value for the `secure` attribute based on your API domain having `http` or `https`.]
:::

<CodeGroup group="backend-language">
<Tab title="Node.js" value="nodejs">
```tsx
import SuperTokens from "supertokens-node";
import Session from "supertokens-node/recipe/session";

SuperTokens.init({
  supertokens: {
    connectionURI: "...",
  },
  appInfo: {
    apiDomain: "...",
    appName: "...",
    websiteDomain: "...",
  },
  recipeList: [
    Session.init({
      cookieSecure: true,
    }),
  ],
});
```
</Tab>
<Tab title="Go" value="go">
```go
import (
	"github.com/supertokens/supertokens-golang/recipe/session"
	"github.com/supertokens/supertokens-golang/recipe/session/sessmodels"
	"github.com/supertokens/supertokens-golang/supertokens"
)

func main() {
	cookieSecure := true

	supertokens.Init(supertokens.TypeInput{
		RecipeList: []supertokens.Recipe{
			session.Init(&sessmodels.TypeInput{
				CookieSecure: &cookieSecure,
			}),
		},
	})
}
```
</Tab>
<Tab title="Python" value="python">
```python check=false reason="Partial configuration example"
from supertokens_python import init, InputAppInfo
from supertokens_python.recipe import session

init(
    app_info=InputAppInfo(api_domain="...", app_name="...", website_domain="..."),
    framework='...',
    recipe_list=[
        session.init(
            cookie_secure=True
        )
    ]
)
```
</Tab>
</CodeGroup>

---


## Access Token signing key rotation

Access Token signing key rotation implies that the secret key for signing the access tokens changes at a fixed time interval. This reduces the risk of key theft.

:::info[- Existing logged in users are not logged out on key change.]
- This feature enables by default.
:::

### Change the key rotation interval


<CodeGroup group="docker">
<Tab title="With Docker" value="with-docker">
```bash
docker run \
  -p 3567:3567 \
  -e ACCESS_TOKEN_DYNAMIC_SIGNING_KEY_UPDATE_INTERVAL=168 \
  -d supertokens/supertokens-<db name>
```
</Tab>
<Tab title="Without Docker" value="without-docker">
```yaml
# You need to add the following to the config.yaml file.
# The file path can be found by running the "supertokens --help" command

access_token_dynamic_signing_key_update_interval: 168
```
</Tab>
</CodeGroup>

- ``access_token_dynamic_signing_key_update_interval``
  - Time in hours for how frequently the signing key changes.
  - It must have a ``number`` value with, the default value set to ``168``

:::info[For managed service, update this value in the **Session Management** configuration card in the relevant deployment's **Configuration** page.]
:::

### Use static keys

If you do not want to use dynamic keys for session creation, then you can tell SuperTokens to use the static key instead. This is useful in cases where you want to [hard-code the public key for JWT verification in some process](/additional-verification/session-verification/protect-api-routes#with-the-public-key-string).

<CodeGroup group="backend-language">
<Tab title="Node.js" value="nodejs">
```tsx
import SuperTokens from "supertokens-node";
import Session from "supertokens-node/recipe/session";

SuperTokens.init({
  supertokens: {
    connectionURI: "...",
  },
  appInfo: {
    apiDomain: "...",
    appName: "...",
    websiteDomain: "...",
  },
  recipeList: [
    Session.init({
      useDynamicAccessTokenSigningKey: false,
    }),
  ],
});
```
</Tab>
<Tab title="Go" value="go">
```go
import (
	"github.com/supertokens/supertokens-golang/recipe/session"
	"github.com/supertokens/supertokens-golang/recipe/session/sessmodels"
	"github.com/supertokens/supertokens-golang/supertokens"
)

func main() {
	useDynamicAccessTokenSigningKey := false

	supertokens.Init(supertokens.TypeInput{
		RecipeList: []supertokens.Recipe{
			session.Init(&sessmodels.TypeInput{
				UseDynamicAccessTokenSigningKey: &useDynamicAccessTokenSigningKey,
			}),
		},
	})
}
```
</Tab>
<Tab title="Python" value="python">
```python check=false reason="Partial configuration example"
from supertokens_python import init, InputAppInfo
from supertokens_python.recipe import session

init(
    app_info=InputAppInfo(api_domain="...", app_name="...", website_domain="..."),
    framework='...',
    recipe_list=[
        session.init(
            use_dynamic_access_token_signing_key=False
        )
    ]
)
```
</Tab>
</CodeGroup>

:::warning[Updating this value causes a spike in the session refresh API, as and when users visit your application.]
:::

---

## See also

<CardGroup cols={3}>
  <Card title="Session Invalidation" href="/post-authentication/session-management/session-invalidation" />
  <Card title="Access Token Blacklisting" href="/post-authentication/session-management/advanced-workflows/access-token-blacklisting" />
  <Card title="Cookies vs. Header Authentication" href="/post-authentication/session-management/switch-between-cookies-and-header-authentication" />
  <Card title="Protect API Routes" href="/additional-verification/session-verification/protect-api-routes" />
  <Card title="Claim Validation" href="/additional-verification/session-verification/claim-validation" />
</CardGroup>
