Skip to main content

Module csrf

Module csrf 

Source
Available on crate feature csrf only.
Expand description

Modern protection against cross-site request forgery (CSRF) attacks.

This middleware implements the CSRF protection scheme introduced in Go 1.25 and described in Filippo Valsorda’s blog post. It relies on the Sec-Fetch-Site and Origin request headers and requires no per-request token state.

Requests are allowed if any of the following hold:

  1. The method is GET, HEAD, or OPTIONS.
  2. The Origin header byte-for-byte matches an allow-listed trusted origin.
  3. Sec-Fetch-Site is same-origin or none.
  4. Neither Sec-Fetch-Site nor Origin is present.
  5. The Origin’s authority (host and any port) matches the request’s effective host byte-for-byte (the request-target authority if present, else Host).

Rejected requests receive a 403 Forbidden response. The originating ProtectionError is attached to the response’s extensions — on every rejection, including those from a custom builder — so surrounding layers can distinguish explicit cross-origin rejections from conservative fallback rejections (e.g. requests from old browsers without Sec-Fetch-Site). Use CsrfLayer::with_rejection_response to replace the rejection response with a custom builder.

§Deployment caveat

The middleware trusts whatever Origin and Host reach it. Reverse proxies and load balancers that rewrite Host (e.g. to an internal hostname) or strip Origin silently degrade the protection: the Origin/Host fallback can no longer match, and Sec-Fetch-Site becomes the only remaining line of defense. Configure intermediaries to forward both headers unchanged.

§Example

use bytes::Bytes;
use http::{Request, Response, StatusCode};
use http_body_util::Full;
use tower::{Service, ServiceExt, ServiceBuilder, service_fn, BoxError};
use tower_http::csrf::CsrfLayer;

async fn handle(_: Request<Full<Bytes>>) -> Result<Response<Full<Bytes>>, BoxError> {
    Ok(Response::new(Full::default()))
}

let layer = CsrfLayer::new()
    .add_trusted_origin("https://example.com")?;

let mut service = ServiceBuilder::new()
    .layer(layer)
    .service_fn(handle);

// Safe methods always pass.
let request = Request::builder()
    .method("GET")
    .uri("/")
    .body(Full::default())
    .unwrap();

let response = service.ready().await?.call(request).await?;

assert_eq!(response.status(), StatusCode::OK);

// Cross-site POSTs are blocked.
let request = Request::builder()
    .method("POST")
    .uri("/")
    .header("host", "example.com")
    .header("sec-fetch-site", "cross-site")
    .body(Full::default())
    .unwrap();

let response = service.ready().await?.call(request).await?;

assert_eq!(response.status(), StatusCode::FORBIDDEN);

Structs§

Csrf
Middleware that enforces cross-origin request forgery (CSRF) protection.
CsrfLayer
Layer that applies the Csrf middleware.
DefaultResponseForProtectionError
Default ResponseForProtectionError used by CsrfLayer::new.
ProtectionError
Reason a request was rejected by Csrf.
ResponseFuture
Response future for Csrf.

Enums§

ConfigError
Errors that can occur while configuring CsrfLayer.
ProtectionErrorKind
The category of a ProtectionError.

Traits§

ResponseForProtectionError
Builds the response returned by Csrf when a request fails CSRF protection.