Add CSRF Middleware
Create a middleware to protect your app from Cross Site Request Forgery (CSRF) attacks.
When using session cookies for authentication, you must protect against CSRF attacks. CSRF is a type of web security vulnerability where an attacker tricks a user into unknowingly submitting a request to a web application where they're already authenticated. Since browsers automatically include cookies with requests, the malicious request appears legitimate to the server. This allows attackers to perform actions on behalf of the user without their consent.
For modern browsers, CSRF attacks can be prevented by using the SameSite cookie attribute. Setting the SameSite cookie attribute to Lax or Strict will ensure that the browser won't send the cookie for cross-site requests, eliminating the threat of CSRF. However, for older browsers that don't support the SameSite cookie attribute, you'll need to implement your own CSRF protection. One common approach for building CSRF protection is to use the Signed Double Submit Cookie pattern.
Implement Signed Double Submit Cookie Pattern
The Signed Double Submit Cookie pattern is a stateless solution for preventing CSRF attacks. For a high-level overview of how the Signed Double Submit Cookie pattern works, please refer to the following documentation.
Install CSRF SDK
To help implement the Signed Double Submit Cookie pattern, we'll use the csrf library.
npm install csrf
yarn add csrf
pnpm add csrf
Add CSRF Utility Functions
Next, you'll need to create a couple of utility functions for use both within the CSRF middleware and the Callback Endpoint.
// csrf.ts
import Tokens from 'csrf';
const csrfTokens = new Tokens();
// Updates the CSRF cookie with a new CSRF token.
export const updateCsrfTokenAndCookie = function (req, res) {
const csrfToken = csrfTokens.create(req.session.csrfSecret);
res.cookie('CSRF-TOKEN', csrfToken, {
httpOnly: false, // Must be false so that your frontend JavaScript code can access the value.
maxAge: 1800000, // This value should match your session cookie expiration, e.g. 30 mins
path: '/',
sameSite: true, // This should match the SameSite settings of your session cookie.
secure: false, // IMPORTANT: Only set this to false if your server isn't using HTTPS.
});
};
// Generates a new CSRF secret.
export const createCsrfSecret = function () {
return csrfTokens.secretSync();
};
// Verifies that the CSRF token in the request header was cryptographically generated from the
// CSRF secret in your user's session.
export const isCsrfTokenValid = function (req) {
const csrfToken = req.headers['x-csrf-token'];
if (!csrfToken || !req.session.csrfSecret){
return false;
}
return csrfTokens.verify(req.session.csrfSecret, csrfToken);
};
Implement Middleware
Next, we'll create a middleware that will check whether the CSRF token in the request header can be computed using the CSRF secret in the session cookie. If the CSRF token verification fails, then a 403 response will be returned.
// csrf-middleware.ts
import { isCsrfTokenValid, updateCsrfTokenAndCookie } from '@/utils/csrf';
const csrfMiddleware = function (req, res, next) {
if (!isCsrfTokenValid(req)) {
return res.status(403).send();
}
// After validation, a new CSRF cookie is generated containing a new CSRF token.
updateCsrfTokenAndCookie(req, res);
return next();
};
Apply CSRF Middleware to All Protected Endpoints
You will need to apply the CSRF middleware to any protected endpoints that require an authenticated user session to access. As such, you'll need to enable the CSRF Middleware for your Session endpoint, as shown below:
// app.ts
import { authMiddleware, csrfMiddleware } from './middleware';
...
// Update your session endpoint to use your CSRF middleware
app.get('/session', [authMiddleware, csrfMiddleware], (req, res) => {
const { email, isAuthenticated } = req.session;
return res.status(200).json({ isAuthenticated, email });
});
...
Put All Protected Endpoints Behind Your CSRF Middleware
Protect all endpoints that require an authenticated user session - not just the Session Endpoint - with the CSRF middleware.
Note: The Login, Callback, and Logout endpoints are meant to be accessed by unauthenticated users. Avoid sticking the CSRF middleware in front of those endpoints.
Updated 15 days ago
To complete our CSRF protection logic, we'll need to make adjustments to our existing auth endpoints.