SyntaxStudy
Sign Up
Web Security CSRF Tokens and the Synchroniser Token Pattern
Web Security Beginner 2 min read

CSRF Tokens and the Synchroniser Token Pattern

The synchroniser token pattern is the most widely deployed CSRF defence. The server generates a cryptographically random token, stores it in the session, and embeds it in every HTML form as a hidden field or in JavaScript-accessible metadata. When the form is submitted, the server compares the submitted token against the session-stored value. A cross-origin attacker cannot read the token from the victim's page due to the Same-Origin Policy, so the forged request will lack a valid token and be rejected. Laravel automatically generates CSRF tokens and validates them on all state-changing HTTP methods (POST, PUT, PATCH, DELETE) through the `VerifyCsrfToken` middleware. The `@csrf` Blade directive inserts the hidden input field. For AJAX requests, the token is available as a meta tag value and must be sent in the `X-CSRF-TOKEN` header. The `csrf_token()` helper returns the current session token. The double-submit cookie pattern is an alternative for stateless APIs where storing a token in the server session is impractical. The server sets a random value in a non-HttpOnly cookie; JavaScript reads this cookie value and includes it in a custom header (e.g., `X-CSRF-TOKEN`) or request body. The server verifies that the header value matches the cookie value. A cross-origin attacker cannot read the cookie (SOP) nor set custom headers on cross-origin requests (CORS blocks it), so the forged request fails.
Example
{{-- Laravel Blade: synchroniser token pattern --}}

{{-- HTML form: @csrf inserts a hidden _token field --}}
<form method="POST" action="/account/email">
    @csrf
    {{-- Expands to: <input type="hidden" name="_token" value="...32-byte-random..."> --}}
    <input type="email" name="email" required>
    <button type="submit">Update Email</button>
</form>

<?php
// Laravel: reading token in JavaScript (for AJAX)
// Place this in your main layout <head>:
// <meta name="csrf-token" content="{{ csrf_token() }}">

// JavaScript: axios — configure globally to send token header
// import axios from 'axios';
// axios.defaults.headers.common['X-CSRF-TOKEN'] =
//     document.querySelector('meta[name="csrf-token"]').content;

// ----------------------------------------------------------------
// Double-submit cookie pattern (for token-based / SPA APIs)
// ----------------------------------------------------------------
// Server sets cookie:
//   Set-Cookie: XSRF-TOKEN=<random>; SameSite=Strict; Secure; Path=/
//
// JavaScript reads cookie and adds header:
// const token = document.cookie.match(/XSRF-TOKEN=([^;]+)/)?.[1];
// fetch('/api/transfer', {
//   method: 'POST',
//   headers: { 'X-XSRF-TOKEN': token, 'Content-Type': 'application/json' },
//   body: JSON.stringify({ amount: 100 }),
//   credentials: 'include'
// });
//
// Server validates: request header value === cookie value → reject if mismatch
?>