CSRF Protection

Cross-Site Request Forgery (CSRF) protection prevents attackers from tricking users into performing unwanted actions.

Enabling CSRF Protection

Add the csrf feature to your Cargo.toml:

[dependencies]
actix-security = { version = "0.2", features = ["csrf"] }

Basic Usage

use actix_security::http::security::{CsrfProtection, CsrfConfig};
use actix_session::{SessionMiddleware, storage::CookieSessionStore};
use actix_web::{App, HttpServer, cookie::Key};

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    let secret_key = Key::generate();

    let csrf = CsrfProtection::new(CsrfConfig::default());

    HttpServer::new(move || {
        App::new()
            // Session middleware is required for CSRF
            .wrap(SessionMiddleware::new(
                CookieSessionStore::default(),
                secret_key.clone(),
            ))
            .wrap(csrf.clone())
            // ... routes
    })
    .bind("127.0.0.1:8080")?
    .run()
    .await
}

Configuration Options

CsrfConfig::new()
    // Name of the form field containing the token
    .token_parameter("_csrf")

    // Name of the HTTP header containing the token
    .header_name("X-CSRF-TOKEN")

    // Session key for storing the token
    .session_key("csrf_token")

    // Paths to exclude from CSRF checks
    .ignore_path("/api/webhook")
    .ignore_paths(vec!["/api/public/*"])

    // Methods that don't require CSRF (safe methods)
    // Default: GET, HEAD, OPTIONS, TRACE
    .ignore_method(Method::GET)

Token in Forms

Include the CSRF token in your HTML forms:

<form method="POST" action="/submit">
    <input type="hidden" name="_csrf" value="{{ csrf_token }}">
    <!-- other form fields -->
    <button type="submit">Submit</button>
</form>

To get the CSRF token in your handler:

use actix_security::http::security::CsrfToken;
use actix_session::Session;

async fn show_form(session: Session) -> impl Responder {
    // Generate or retrieve CSRF token
    let token = CsrfToken::generate();
    session.insert("csrf_token", &token.token).ok();

    let html = format!(r#"
        <form method="POST" action="/submit">
            <input type="hidden" name="_csrf" value="{}">
            <button type="submit">Submit</button>
        </form>
    "#, token.token);

    HttpResponse::Ok().content_type("text/html").body(html)
}

Token in AJAX Requests

For JavaScript/AJAX requests, include the token in a header:

// Get token from meta tag or cookie
const csrfToken = document.querySelector('meta[name="csrf-token"]').content;

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

Token Repository

By default, tokens are stored in the session. You can customize this:

use actix_security::http::security::{CsrfTokenRepository, SessionCsrfTokenRepository};

// Session-based (default)
CsrfConfig::new()
    .token_repository(SessionCsrfTokenRepository::new())

Error Handling

When CSRF validation fails, a 403 Forbidden response is returned by default.

use actix_security::http::security::CsrfError;

// CsrfError variants:
CsrfError::MissingToken    // No token in request
CsrfError::InvalidToken    // Token doesn't match
CsrfError::SessionError    // Session storage issue

When to Use CSRF Protection

  • Enable for: Form submissions, state-changing operations
  • Disable for: APIs using token-based auth (JWT), webhooks, public endpoints

Spring Security Comparison

Spring SecurityActix Security
CsrfFilterCsrfProtection middleware
CsrfTokenCsrfToken
CsrfTokenRepositoryCsrfTokenRepository trait
HttpSessionCsrfTokenRepositorySessionCsrfTokenRepository
.csrf().disable().ignore_path()