Skip to main content
Kit provides automatic CSRF (Cross-Site Request Forgery) protection for all state-changing requests. This prevents malicious websites from executing actions on behalf of authenticated users.

Overview

CSRF protection in Kit:
  • Automatically validates all POST, PUT, PATCH, and DELETE requests
  • Generates per-session tokens for maximum security
  • Integrates with Inertia.js via meta tag and axios interceptor
  • Uses constant-time comparison to prevent timing attacks

How It Works

  1. When a session is created, Kit generates a cryptographically secure CSRF token
  2. This token is embedded in the HTML via a <meta> tag
  3. The frontend automatically includes the token in request headers
  4. Kit validates the token on every state-changing request
  5. Invalid tokens result in a 419 “Page Expired” response

Frontend Integration

Automatic Setup

Kit projects are pre-configured with CSRF protection. The generated main.tsx sets up axios to automatically include the CSRF token:
// frontend/src/main.tsx
import axios from 'axios';

// Get CSRF token from meta tag
const token = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content');
if (token) {
  axios.defaults.headers.common['X-CSRF-TOKEN'] = token;
}

Inertia Forms

When using Inertia’s useForm hook, CSRF tokens are automatically included:
import { useForm } from '@inertiajs/react';

function CreatePost() {
  const { data, setData, post } = useForm({
    title: '',
    content: '',
  });

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    post('/posts');  // CSRF token automatically included
  };

  return (
    <form onSubmit={handleSubmit}>
      {/* ... */}
    </form>
  );
}

Manual Requests

For manual fetch or axios requests, include the token from the meta tag:
const csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content');

// Using fetch
fetch('/api/data', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-CSRF-TOKEN': csrfToken || '',
  },
  body: JSON.stringify({ /* ... */ }),
});

// Using axios (already configured by default)
axios.post('/api/data', { /* ... */ });

Backend Configuration

CSRF middleware is automatically registered in bootstrap.rs:
use kit::{Router, SessionMiddleware, CsrfMiddleware};

pub fn boot(router: Router) -> Router {
    router
        // Session middleware must come first
        .middleware(SessionMiddleware::new(session_store, session_config))
        // CSRF protection
        .middleware(CsrfMiddleware::new())
}

Excluding Routes

Some routes may need to bypass CSRF protection (e.g., webhook endpoints). You can exclude specific routes:
use kit::CsrfMiddleware;

// In your bootstrap or route configuration
let csrf = CsrfMiddleware::new()
    .except("/webhooks/stripe")
    .except("/webhooks/github");

CSRF Helper Functions

Kit provides helper functions for working with CSRF tokens:
use kit::csrf::{csrf_token, csrf_meta_tag};

// Get the current CSRF token
if let Some(token) = csrf_token() {
    println!("Token: {}", token);
}

// Generate a meta tag (used internally by Inertia)
let meta = csrf_meta_tag();
// Returns: <meta name="csrf-token" content="...">

Error Handling

When CSRF validation fails, Kit returns a 419 status code with a “CSRF token mismatch” message. You can customize this behavior:
// In your frontend error handling
axios.interceptors.response.use(
  response => response,
  error => {
    if (error.response?.status === 419) {
      // Token expired - reload the page to get a new token
      window.location.reload();
    }
    return Promise.reject(error);
  }
);

Security Considerations

Kit’s CSRF implementation follows security best practices:
  • Per-session tokens: Each session has its own unique CSRF token
  • Secure generation: Tokens are generated using cryptographically secure random bytes
  • Constant-time comparison: Token validation uses constant-time comparison to prevent timing attacks
  • Token regeneration: Tokens are regenerated on logout to prevent session fixation
  • SameSite cookies: Combined with SameSite=Lax cookies for defense in depth

Testing

When writing tests, you’ll need to include CSRF tokens. Kit’s testing utilities handle this automatically:
#[tokio::test]
async fn test_protected_route() {
    let app = test_app().await;

    // First, get a session
    let response = app.get("/login").await;
    let csrf_token = response.csrf_token();

    // Include token in POST request
    let response = app
        .post("/login")
        .header("X-CSRF-TOKEN", csrf_token)
        .json(&json!({
            "email": "[email protected]",
            "password": "password"
        }))
        .await;

    assert_eq!(response.status(), 302);
}

Inertia-Specific Behavior

When using Inertia.js, CSRF handling has some special considerations:
  • The CSRF token is injected into the HTML page via a <meta> tag
  • Inertia automatically reads this token and includes it in XHR requests
  • For 419 responses, Inertia can be configured to handle the redirect:
// In your createInertiaApp setup
createInertiaApp({
  resolve: name => {/* ... */},
  setup({ el, App, props }) {
    // Handle 419 errors globally
    // ...
  },
});