SyntaxStudy
Sign Up
Web Security SameSite Cookies: Browser-Enforced CSRF Protection
Web Security Beginner 1 min read

SameSite Cookies: Browser-Enforced CSRF Protection

The `SameSite` cookie attribute instructs the browser when to include a cookie in cross-site requests. `SameSite=Strict` means the cookie is never sent with cross-site requests — not even when clicking a link from an external site to your application, which can disrupt user experience for shared links. `SameSite=Lax` (now the browser default for cookies without an explicit SameSite value) allows the cookie on top-level navigations (clicking links) but blocks it on sub-resource requests from third-party pages, stopping most CSRF attacks. `SameSite=None; Secure` explicitly opts into sending the cookie cross-site, required for embedded widgets and third-party integrations. `SameSite=Lax` alone is not a complete CSRF defence because it still sends the cookie on top-level GET navigations. Applications must ensure that GET requests never cause state changes. Combine SameSite cookies with CSRF tokens for defence in depth: the token covers legacy browsers that do not support SameSite, and SameSite provides protection even if the token implementation has gaps. Cookie security involves multiple attributes working together. `Secure` ensures the cookie is only sent over HTTPS, preventing interception on mixed networks. `HttpOnly` prevents JavaScript from reading the cookie, blocking XSS-based cookie theft. `SameSite` blocks CSRF. Setting all three — `Secure; HttpOnly; SameSite=Lax` — on session cookies is the baseline for any production application.
Example
<?php
// PHP: setting a hardened session cookie

session_set_cookie_params([
    'lifetime' => 0,          // expire when browser closes
    'path'     => '/',
    'domain'   => 'example.com',
    'secure'   => true,       // HTTPS only
    'httponly' => true,       // no JavaScript access
    'samesite' => 'Lax',      // CSRF protection; use 'Strict' for high-security apps
]);
session_start();

// Or set an arbitrary cookie:
setcookie('user_pref', 'dark_mode', [
    'expires'  => time() + 86400 * 30,
    'path'     => '/',
    'secure'   => true,
    'httponly' => false,       // needs to be JS-readable? only if necessary
    'samesite' => 'Lax',
]);

// Laravel config/session.php equivalents:
// 'secure'    => env('SESSION_SECURE_COOKIE', true),
// 'http_only' => true,
// 'same_site' => 'lax',

// ----------------------------------------------------------------
// Set-Cookie header examples (what the browser receives):
// ----------------------------------------------------------------
// Session cookie (most secure):
//   Set-Cookie: session=abc123; Path=/; Secure; HttpOnly; SameSite=Strict
//
// Third-party/embedded widget cookie (cross-site allowed):
//   Set-Cookie: widget_token=xyz; Path=/; Secure; SameSite=None
//   (Note: SameSite=None REQUIRES Secure — browsers reject it otherwise)