Step-by-Step Guide
5 steps

How to Implement CSRF Protection

CSRF attacks trick authenticated users into making unwanted requests to your application. If a user is logged in to your banking app and visits a malicious site, that site could submit a transfer request on their behalf. This guide covers defense strategies.

Find security issues automatically before attackers do.

Follow These Steps

1

Use SameSite cookies as the first line of defense

SameSite cookies prevent most CSRF attacks by limiting when cookies are sent.

Code Example
res.cookie('session', token, {
  httpOnly: true,
  secure: true,
  sameSite: 'lax',  // Sent with top-level navigations, blocked for cross-origin POSTs
  maxAge: 86400000
})

Use SameSite=Lax for most apps. Use SameSite=Strict only if your app does not use OAuth (OAuth redirects need Lax).

2

Check the Origin header on state-changing requests

Verify that the Origin header matches your domain for POST, PUT, DELETE requests.

Code Example
function validateOrigin(req: Request): boolean {
  const origin = req.headers.get('origin')
  const allowedOrigins = ['https://yourdomain.com']
  return !origin || allowedOrigins.includes(origin)
}

export async function POST(req: Request) {
  if (!validateOrigin(req)) {
    return Response.json({ error: 'Invalid origin' }, { status: 403 })
  }
  // Process request
}
3

Implement CSRF tokens for traditional forms

For server-rendered forms, include a CSRF token that must match the server-side value.

Code Example
import csrf from 'csurf'

// Express.js
const csrfProtection = csrf({ cookie: true })

app.get('/form', csrfProtection, (req, res) => {
  res.render('form', { csrfToken: req.csrfToken() })
})

app.post('/submit', csrfProtection, (req, res) => {
  // CSRF token is automatically validated
  res.json({ success: true })
})

// In HTML form
<form method="POST" action="/submit">
  <input type="hidden" name="_csrf" value="{{csrfToken}}" />
  <button type="submit">Submit</button>
</form>
4

Use the Double Submit Cookie pattern for SPAs

For single-page apps, generate a CSRF token cookie and require it as a header on requests.

Code Example
// Server: Set CSRF cookie on login
res.cookie('csrf-token', generateToken(), {
  httpOnly: false,  // JS needs to read this
  secure: true,
  sameSite: 'lax'
})

// Client: Read cookie and send as header
const csrfToken = document.cookie
  .split('; ')
  .find(c => c.startsWith('csrf-token='))
  ?.split('=')[1]

fetch('/api/action', {
  method: 'POST',
  headers: { 'X-CSRF-Token': csrfToken || '' }
})

// Server: Verify header matches cookie
function verifyCsrf(req) {
  return req.cookies['csrf-token'] === req.headers['x-csrf-token']
}
5

Verify protection is working

Test that cross-origin requests are properly blocked.

Code Example
# Test from a different origin (should fail)
curl -X POST https://yourdomain.com/api/action \
  -H "Origin: https://evil.com" \
  -H "Cookie: session=..." \
  -v
# Should return 403

What You'll Achieve

Your application uses SameSite cookies, Origin header validation, and CSRF tokens to prevent cross-site request forgery attacks on all state-changing endpoints.

Common Mistakes to Avoid

Mistake

Using SameSite=None without the Secure flag

Fix

SameSite=None requires the Secure flag. Without it, the cookie will be rejected by browsers.

Mistake

Using SameSite=Strict with OAuth

Fix

Strict blocks the cookie on OAuth redirects. Use Lax for apps that use OAuth or any external redirects.

Mistake

Only protecting POST endpoints

Fix

Protect all state-changing methods: POST, PUT, PATCH, DELETE. GET requests should never modify state.

Frequently Asked Questions

Do SPAs need CSRF protection?

If your SPA uses cookie-based authentication (most do), yes. If you use Authorization headers with tokens stored in memory (not cookies), CSRF protection is not needed since the attacker cannot set custom headers.

Is SameSite=Lax enough?

SameSite=Lax prevents most CSRF attacks by blocking cross-origin POST requests with cookies. For additional defense, add Origin header validation. Full CSRF tokens are needed mainly for legacy browser support.

Ready to Secure Your App?

VAS automatically scans your deployed app for the security issues covered in this guide. Get actionable results in minutes.

Start Security Scan