Cross-Site Scripting (XSS) and Cross-Site Request Forgery (CSRF)Lesson 3.4
Implementing CSRF tokens with the double-submit cookie pattern
CSRF token mechanics, synchronizer token pattern, double-submit cookie, csrf npm package, token validation, per-session vs per-request tokens
CSRF Tokens
A CSRF token is an unguessable value tied to a user's session that must be included with every state-changing request. An attacker cannot read it from another origin due to the Same-Origin Policy, so they cannot forge valid requests.
Using the csurf Package
const csrf = require('csurf');
const cookieParser = require('cookie-parser');
app.use(cookieParser());
// Cookie-based CSRF (stateless, works with REST APIs)
const csrfProtection = csrf({ cookie: true });
// Generate and expose token on GET
app.get('/form', csrfProtection, (req, res) => {
res.json({ csrfToken: req.csrfToken() });
});
// Validate token on state-changing POST
app.post('/transfer', csrfProtection, (req, res) => {
// If token missing or invalid, csurf throws a 403 automatically
res.json({ status: 'Transfer complete' });
});Frontend: Send the Token as a Header
// Fetch the CSRF token first
const { csrfToken } = await fetch('/form').then(r => r.json());
// Include it on every mutating request
await fetch('/transfer', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': csrfToken // Custom header that cross-site forms cannot set
},
body: JSON.stringify({ amount: 100 })
});The double-submit cookie pattern requires no server-side session storage. The token is in a cookie (readable by your JavaScript, inaccessible to other origins) and sent in a request header. The server compares them—an attacker from another origin cannot set the header because they cannot read the cookie.
