Research
Security News
Malicious npm Packages Inject SSH Backdoors via Typosquatted Libraries
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
@otterhttp/csrf-csrf
Advanced tools
Double-Submit Cookie Pattern CSRF Protection middleware for modern Node.js
Double-submit cookie pattern CSRF protection middleware for modern Node.js.
:pushpin: This project is a fork of Psifi-Solutions/csrf-csrf.
Dos and Don'ts • Getting Started • Configuration • Utilities • Support
This module provides the necessary pieces required to implement CSRF protection using the Double Submit Cookie Pattern. This is a stateless CSRF protection pattern, if you are using sessions and would prefer a stateful CSRF strategy, please see csrf-sync for the Synchroniser Token Pattern.
fastify/csrf-protection
recommendations for secret security.secure: true
in production.csrf-csrf
and cookie-parser
.This section will guide you through using the default setup, which does sufficiently implement the Double Submit Cookie Pattern. If you'd like to customise the configuration, see configuration.
You will need to be using tinyhttp/cookie-parser whose middleware
should be registered before csrf-csrf
.
In case you want to use signed CSRF cookies, you will need to provide cookie-parser
with a unique secret
for cookie signing.
This utility will (1) set a cookie containing both the csrf token and a hash of the csrf token, and
(2) provide the plain csrf token.
You are then responsible for including the CSRF token within your response however you choose.
npm install @tinyhttp/cookie-parser @otterjs/csrf-csrf
// ESM
import { doubleCsrf } from "@otterjs/csrf-csrf";
// CommonJS
const { doubleCsrf } = require("@otterjs/csrf-csrf");
const {
invalidCsrfTokenError, // This is just for convenience if you plan on making your own middleware.
generateToken, // Use this in your routes to provide a CSRF hash + token cookie and token.
validateRequest, // Also a convenience if you plan on making your own middleware.
doubleCsrfProtection, // This is the default CSRF protection middleware.
} = doubleCsrf(doubleCsrfOptions);
The doubleCsrf
method will provide the default utilities, you can configure these and re-export them from your own module.
You should only transmit your token to the frontend as part of a response body, do not include the token in
response headers or in a cookie, and do not transmit the token hash by any other means.
To create a route which generates a CSRF token and a cookie containing ´${token}|${tokenHash}´
:
const myCsrfExchangeRoute = (req, res) => {
const csrfToken = generateToken(req, res);
// You could also pass the token into the context of a HTML template response.
return res.json({ csrfToken });
}
You can also put the token into the context of a templated HTML response.
If you use an HTTP verb other than GET
, make sure you register this route before registering the
doubleCsrfProtection
middleware so you don't block yourself from getting a token.
// Make sure your session middleware is registered before these
express.use(session);
express.get("/csrf-token", myRoute);
express.use(doubleCsrfProtection);
// Any non GET routes registered after this will be considered "protected"
By default, any request that are not GET
, HEAD
, or OPTIONS
methods will be
protected. You can configure this with the ignoredMethods
option.
You can also protect routes on a case-to-case basis:
app.get("/secret-stuff", doubleCsrfProtection, myProtectedRoute);
Once a route is protected, you will need to ensure the hash cookie is sent along with the request and by default
you will need to include the generated token in the x-csrf-token
header, otherwise you'll receive
a 403 - ForbiddenError: invalid csrf token
. If your cookie is not being included in your requests be sure to
check your withCredentials
and CORS configuration.
If you plan on using session middleware then please ensure your cookie-parsing middleware is registered after your session middleware. Your session middleware may parse its own cookies and therefore may conflict with your cookie parsing middleware.
When configuring, the only required options are getSecret
and getSessionIdentifier
, the rest have sensible
defaults (shown below).
const doubleCsrfUtilities = doubleCsrf({
getSecret: () => "Secret", // A function that optionally takes the request and returns a secret
getSessionIdentifier: (req) => req.session.id, // A function that returns the session identifier for the request
cookieOptions: {
name: "__Host-otter.x-csrf-token", // The name of the cookie to be used, recommend using __Host prefix
sameSite: "lax", // Recommend you make this strict if posible
path: "/",
secure: true,
...remainingCookieOptions // See cookieOptions below
},
size: 64, // The size of the generated tokens in bits
ignoredMethods: ["GET", "HEAD", "OPTIONS"], // A list of request methods that will not be protected.
getTokenFromRequest: (req) => req.headers["x-csrf-token"], // A function that returns the token from the request
});
getSecret
type GetSecretType = (request?: Request) => string | string[]
Required
This should return a secret key or an array of secret keys to be used for hashing the CSRF tokens.
In case multiple are provided, the first one will be used for hashing. For validation, all secrets will be tried, preferring the first one in the array. Having multiple valid secrets can be useful when you need to rotate secrets, but you don't want to invalidate the previous secret (which might still be used by some users) right away.
getSessionIdentifier
type GetSessionIdentifierType = (request: Request) => string;
Required
This function should return the session identifier for the incoming request. This is used as part of the CSRF token hash to ensure generated tokens can only be used by the sessions that originally requested them.
If you are rotating your sessions, you will need to ensure a new CSRF token is generated at the same time. This should typically be done when a session has some sort of authorization elevation (e.g. signed in, signed out, sudo).
cookieOptions
type CookieOptions = SerializeOptions & {
name?: string
sameSite?: string;
path?: string;
secure?: boolean;
signed?: boolean;
}
Optional
Default:
const defaultCookieOptions = {
name: "__Host-otter.x-csrf-token",
sameSite: "lax",
path: "/",
secure: true,
signed: false,
}
The options used when serializing the CSRF exchange cookie (see cookie attributes).
name
The name of the cookie that will be used to track CSRF protection.
If you change this it is recommended that you continue to use the __Host
or __Secure
security prefix.
signed
Whether to sign CSRF exchange cookies.
When this option is enabled, you also need to provide your cookie parsing middleware with a unique secret for cookie signing.
The __Host
security prefix requires the secure
flag to be true
and requires requests to be served via HTTPS.
Unless you have your local instance running via HTTPS, you will need to change the cookie name
in your
development environment to omit the security prefix.
You will need to set secure
to false unless you're running HTTPS locally.
Ensure secure
is true in your live environment by using environment variables.
The remaining options are all undefined by default and consist of (at least):
type RemainingCookieOptions = {
maxAge?: number | undefined;
expires?: Date | undefined;
domain?: string | undefined;
encode?: (val: string) => string
}
delimiter
type DelimiterType = string;
Optional
Default: "|"
The delimiter is used when concatenating the plain CSRF token with the hash, constructing the value for the cookie. It is also used when splitting the cookie value. This is how a token can be reused when there is no state. Note that the plain token value within the cookie is only intended to be used for token re-use, it is not used as the source for token validation.
getTokenFromRequest
(req: Request) => string | null | undefined;
Optional
Default:
(req: Request) => req.headers["x-csrf-token"];
This function should return the token sent by the frontend, the doubleCsrfProtection middleware will validate the value returned by this function against the value in the cookie.
hmacAlgorithm
type HmacAlgorithmType = string;
Optional
Default: "sha256"
The algorithm passed to the createHmac
call when generating a token.
ignoredMethods
type IgnoredMethodsType = Array<RequestMethod>;
Optional
Default: ["GET", "HEAD", "OPTIONS"]
An array of request types that the doubleCsrfProtection
middleware will ignore.
Requests made matching these request methods will not be protected.
It is recommended you leave this as the default.
size
type SizeType = number;
Optional
Default: 64
The size in bytes of the tokens that will be generated. If you plan on re-generating tokens, consider reducing this to 32.
errorConfig
type ErrorConfigType = {
statusCode?: number;
message?: string;
code?: string | undefined;
}
Optional
Default:
const defaultErrorConfig = {
statusCode: 403,
message: "invalid csrf token",
code: "ERR_BAD_CSRF_TOKEN"
}
Used to customise the error response statusCode
, the contained error message
, and its code
.
The error is constructed with createHttpError
.
Below is the documentation for what doubleCsrf returns.
doubleCsrfProtection
type DoubleCsrfProtection = (request: Request, response: Response, next: () => void) => void
The middleware used to actually protect your routes (see the 'getting started' examples above , or the examples included in the repository).
generateToken
type GenerateTokenType = (
request: Request,
response: Response,
options?: {
cookieOptions?: CookieOptions, // overrides cookieOptions previously configured just for this call
overwrite?: boolean, // Set to true to force a new token to be generated
validateOnReuse?: boolean, // Set to false to generate a new token if token re-use is invalid
}
) => string;
The function that establishes a CSRF (Cross-Site Request Forgery) protection mechanism by generating a token and issuing a cookie.
It returns a CSRF token and attaches a cookie to the response object.
The cookie content is ${token}${delimiter}${tokenHash}
.
You should only transmit your token to the frontend as part of a response payload. Do not include the token in response headers or in a cookie, and do not transmit the token hash by any means other than the CSRF exchange cookie.
By default, if a csrf-csrf
cookie already exists on an incoming request, generateToken
will not overwrite it.
Instead, it will return the existing token so long as the token is valid.
If you wish to force a token generation, you can set the overwrite
option:
generateToken(req, res, { overwrite: true }); // This will force a new token to be generated, and a new cookie to be set, even if one already exists
If the 'overwrite' parameter is set to false (default), the existing token will be re-used and returned.
However, the cookie value will still be validated.
If the validation fails, an error will be thrown.
If you don't want an error to be thrown, you can set the validateOnReuse
option to false
(it is true
by default).
In this case, a new token will be generated and returned to replace the invalid token.
generateToken(req, res, { overwrite: true }); // As overwrite is true, an error will never be thrown.
generateToken(req, res, { overwrite: false }); // As validateOnReuse is true (default), an error will be thrown if the cookie is invalid.
generateToken(req, res, { overwrite: false, validateOnReuse: false }); // As validateOnReuse is false, if the cookie is invalid a new token will be generated without any error being thrown and despite overwrite being false
invalidCsrfTokenError
type InvalidCsrfTokenError = Error & {
code: string
}
This is the error instance that will be thrown should CSRF token verification fail.
This error is customizable via errorConfig
.
validateToken
type ValidateToken = (req: Request) => boolean;
This function is used by the doubleCsrfProtection middleware to determine whether an incoming request has a valid CSRF token. You can use this to make your own custom middleware (not recommended).
FAQs
Double-Submit Cookie Pattern CSRF Protection middleware for modern Node.js
The npm package @otterhttp/csrf-csrf receives a total of 2 weekly downloads. As such, @otterhttp/csrf-csrf popularity was classified as not popular.
We found that @otterhttp/csrf-csrf demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
Security News
MITRE's 2024 CWE Top 25 highlights critical software vulnerabilities like XSS, SQL Injection, and CSRF, reflecting shifts due to a refined ranking methodology.
Security News
In this segment of the Risky Business podcast, Feross Aboukhadijeh and Patrick Gray discuss the challenges of tracking malware discovered in open source softare.