Security
Learn how to protect your website from CSRF attacks using the `sameSite` cookie attribute.
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
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 to prevent CSRF protection. This sets automatically ifsameSiteisnoneor if yourapiDomainandwebsiteDomaindo 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 aniframe.
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"
}),
],
});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,
}),
},
})
}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'
)
]
)Cookie consent
Per GDPR, 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:
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
APIdomains do not share the same top-level domain, then cookies do not go. Please check the session management page to see how you can switch to using headers.
lax- Cookies are only sent in a first-party context and along with
GETrequests initiated by third party websites (that result in browser navigation - user clicking on a link).
- Cookies are only sent in a first-party context and along with
strict- Cookies are only sent in a first-party context and not sent along with requests initiated by third party websites.
Configuration
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"
}),
],
});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,
}),
},
})
}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'
)
]
)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:
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,
}),
],
});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,
}),
},
})
}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
)
]
)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.
Change the key rotation interval
docker run \
-p 3567:3567 \
-e ACCESS_TOKEN_DYNAMIC_SIGNING_KEY_UPDATE_INTERVAL=168 \
-d supertokens/supertokens-<db name># 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: 168access_token_dynamic_signing_key_update_interval- Time in hours for how frequently the signing key changes.
- It must have a
numbervalue with, the default value set to168
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.
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,
}),
],
});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,
}),
},
})
}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
)
]
)