CSRF Protection

Protection against Cross-Site Request Forgery attacks

sequenceDiagram participant User participant Browser participant Laravel participant Attacker User->>Laravel: Request Form Page Laravel->>Browser: Generate CSRF Token Browser->>Laravel: Submit Form + CSRF Token Laravel->>Laravel: Verify Token Laravel->>Browser: Success Response Attacker->>Laravel: Submit Without Token Laravel->>Attacker: 419 CSRF Token Mismatch

CSRF Protection in Laravel prevents Cross-Site Request Forgery attacks. This type of attack occurs when a malicious site tricks a user into sending unwanted requests to your site.


How it works:


Laravel creates a unique token for each session. This token must be sent in all POST/PUT/PATCH/DELETE forms. Laravel validates this token and rejects the request if it's not valid.


Features:


  • Automatic token generation for each session
  • Automatic validation in middleware
  • Token rotation for more security
  • Excluding specific routes (like webhooks)
  • Support for AJAX requests
  • Double Submit Cookie pattern

Usage:


  • <code>@csrf</code> directive in Blade templates
  • CSRF token in AJAX requests
  • Excluding specific routes from CSRF protection
  • Custom CSRF token handling

Examples

Usage in Blade

<form method="POST" action="/users">
    @csrf
    <input type="text" name="name">
    <button type="submit">Submit</button>
</form>

<!-- Generates: -->
<!-- <input type="hidden" name="_token" value="..."> -->

@csrf directive creates a hidden input with CSRF token.

Usage in AJAX

<!-- In layout -->
<meta name="csrf-token" content="{{ csrf_token() }}">

<!-- In JavaScript -->
$.ajaxSetup({
    headers: {
        'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
    }
});

// Or per request
$.ajax({
    url: '/users',
    method: 'POST',
    headers: {
        'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
    },
    data: { name: 'John' }
});

Setting CSRF token for all AJAX requests.

Exclude Routes from CSRF

<?php

namespace App\Http\Middleware;

use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;

class VerifyCsrfToken extends Middleware
{
    protected $except = [
        'webhook/*',
        'api/payment/callback',
        'stripe/webhook',
    ];
}

Excluding specific routes from CSRF protection (like webhooks).

Custom CSRF Token Handling

<?php

namespace App\Http\Middleware;

use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;

class VerifyCsrfToken extends Middleware
{
    protected function tokensMatch($request)
    {
        // Custom token matching logic
        $token = $request->input('_token') ?: $request->header('X-CSRF-TOKEN');
        
        return hash_equals(
            $request->session()->token(),
            $token
        );
    }
    
    protected function addCookieToResponse($request, $response)
    {
        // Custom cookie handling
        $response->cookie('XSRF-TOKEN', csrf_token(), 60 * 24 * 7);
        
        return $response;
    }
}

Custom handling for CSRF token validation.

CSRF Token in API

<?php

// API routes typically don't use CSRF protection
// They use token-based authentication instead

// In routes/api.php
Route::middleware('auth:sanctum')->group(function () {
    Route::post('/users', [UserController::class, 'store']);
    // No CSRF token needed - uses Sanctum token
});

// But if you need CSRF for API
Route::middleware(['api', 'csrf'])->group(function () {
    // CSRF protected API routes
});

CSRF protection in API routes which typically use token authentication.

CSRF Token Rotation

<?php

// Laravel automatically rotates CSRF token
// But you can customize it

// In VerifyCsrfToken middleware
protected function regenerateToken($request)
{
    $request->session()->regenerateToken();
}

// Manual token regeneration
session()->regenerateToken();

// Get current token
$token = csrf_token();

Token rotation for more security.

CSRF in SPA

<!-- In SPA (Single Page Application) -->
<!-- Fetch token on app load -->

// In JavaScript
async function getCsrfToken() {
    const response = await fetch('/csrf-token');
    const data = await response.json();
    return data.token;
}

// Use in requests
const token = await getCsrfToken();
fetch('/api/users', {
    method: 'POST',
    headers: {
        'X-CSRF-TOKEN': token,
        'Content-Type': 'application/json'
    },
    body: JSON.stringify({ name: 'John' })
});

// Or use cookie-based approach
// Laravel sets XSRF-TOKEN cookie automatically

Using CSRF token in Single Page Applications.

Testing CSRF Protection

<?php

namespace Tests\Feature;

use Tests\TestCase;

class CsrfTest extends TestCase
{
    public function test_csrf_protection_blocks_request()
    {
        $response = $this->post('/users', [
            'name' => 'John'
            // No CSRF token
        ]);
        
        $response->assertStatus(419);
    }
    
    public function test_csrf_protection_allows_valid_token()
    {
        $response = $this->post('/users', [
            'name' => 'John',
            '_token' => csrf_token()
        ]);
        
        $response->assertStatus(200);
    }
}

Testing CSRF protection in tests.

Use Cases

  • Protecting form submissions from CSRF attacks
  • Protecting state-changing operations
  • Webhook endpoints that need to be excluded
  • API endpoints using token authentication
  • SPA applications requiring CSRF token
  • AJAX requests that change state

Common Mistakes

  • Forgetting @csrf in forms causing 419 error
  • Excluding sensitive routes from CSRF causing security risk
  • Not setting CSRF token for AJAX requests
  • Using CSRF in API that uses token auth
  • Forgetting token refresh in SPA
  • Not handling 419 error in frontend

Best Practices

  • Always use @csrf in forms
  • Set CSRF token for AJAX requests
  • Only exclude safe routes (like webhooks)
  • Use token rotation for more security
  • Handle 419 error in frontend
  • Use cookie-based approach for SPA
  • Test CSRF protection

Edge Cases

  • CSRF token expiration in long-running forms
  • Multiple tabs sharing token
  • CSRF in iframe causing issues
  • CSRF token in file uploads
  • CSRF in API using session
  • Token mismatch in load-balanced servers

Performance Notes

  • CSRF validation overhead is very low
  • Token generation and validation is fast
  • Session-based tokens require session storage
  • Cookie-based tokens are better for SPA

Security Notes

  • CSRF protection is essential for state-changing operations
  • Only exclude safe routes
  • Use token rotation for more security
  • Ensure token is stored in secure cookie
  • Use SameSite cookie attribute
  • Don't disable CSRF protection except in special cases

Interview Points

  • What is CSRF attack and how does it work?
  • How does Laravel implement CSRF protection?
  • What is the difference between session-based and cookie-based CSRF token?
  • When should you exclude route from CSRF?
  • How can you use CSRF token in AJAX?
  • What is token rotation and why is it important?

Version Notes

  • Laravel 11.x: Improved performance in CSRF validation
  • Laravel 11.x: Better support for cookie-based tokens
  • Laravel 10.x: Improved token rotation mechanism
  • Laravel 9.x: Improved SameSite cookie support