A crate built on top of `axum-sessions`, implementing the CSRF Synchronizer Token Pattern

Overview

Axum Synchronizer Token Pattern CSRF prevention

This crate provides a Cross-Site Request Forgery protection layer and middleware for use with the axum web framework.

Crates.io Documentation Continuous integration Dependency status

The middleware implements the CSRF Synchronizer Token Pattern for AJAX backends and API endpoints as described in the OWASP CSRF prevention cheat sheet.

More information about this crate can be found in the crate documentation.

Installation

axum-csrf-sync-pattern = "0.2.2"

Examples

See the example projects for same-site and cross-site usage.

Consider as well to use the crate unit tests as your reference.

Scope

This middleware implements token transfer via custom request headers.

The middleware requires and is built upon axum_sessions, which in turn uses async_session.

The current version is built for and works with axum 0.6.x, axum-sessions 0.4.x and async_session 3.x.

There will be support for axum 0.7 and later versions.

The Same Origin Policy prevents the custom request header to be set by foreign scripts.

In which contexts should I use this middleware?

The goal of this middleware is to prevent cross-site request forgery attacks specifically in applications communicating with their backend by means of the JavaScript fetch() API or classic XmlHttpRequest, traditionally called "AJAX".

The Synchronizer Token Pattern is especially useful in CORS contexts, as the underlying session cookie is obligatorily secured and inaccessible by JavaScript, while the custom HTTP response header carrying the CSRF token can be exposed using the CORS Access-Control-Expose-Headers HTTP response header.

While the Same Origin Policy commonly prevents custom request headers to be set on cross-origin requests, use of the use of the Access-Control-Allow-Headers CORS HTTP response header can be used to specifically allow CORS requests to be equipped with a required custom HTTP request header.

This approach ensures that requests forged by auto-submitted forms or other data-submitting scripts from foreign origins are unable to add the required header.

When should I use other CSRF protection patterns or libraries?

Use other available middleware libraries if you plan on submitting classical HTML forms without the use of JavaScript, and if you do not send the form data across origins.

Security

Token randomness

The CSRF tokens are generated using rand::ThreadRng which is considered cryptographically secure (CSPRNG). See "Our RNGs" for more.

Underlying session security

The security of the underlying session is paramount - the CSRF prevention methods applied can only be as secure as the session carrying the server-side token.

  • When creating your SessionLayer, make sure to use at least 64 bytes of cryptographically secure randomness.
  • Do not lower the secure defaults: Keep the session cookie's secure flag on.
  • Use the strictest possible same-site policy.

CORS security

If you need to provide and secure cross-site requests:

  • Allow only your backend origin when configuring the CorsLayer
  • Allow only the headers you need. (At least the CSRF request token header.)
  • Only expose the headers you need. (At least the CSRF response token header.)

No leaks of error details

Errors are logged using [tracing::error!]. Error responses do not contain error details.

Use tower_http::TraceLayer to capture and view traces.

Safety

This crate uses no unsafe code.

The layer and middleware functionality is tested. View the module source code to learn more.

Usage

See the example projects for same-site and cross-site usage. These examples are interactive demos. Run them, then interact with them in the browser.

Same-site usage

Note: The crate repository contains example projects for same-site and cross-site usage! In each example directory, execute cargo run, then open http://127.0.0.1:3000 in your browser.

Configure your session and CSRF protection layer in your backend application:

use axum::{
    body::Body,
    http::StatusCode,
    routing::{get, Router},
};
use axum_csrf_sync_pattern::{CsrfLayer, RegenerateToken};
use axum_sessions::{async_session::MemoryStore, SessionLayer};
use rand::RngCore;

let mut secret = [0; 64];
rand::thread_rng().try_fill_bytes(&mut secret).unwrap();

async fn handler() -> StatusCode {
    StatusCode::OK
}

let app = Router::new()
 .route("/", get(handler).post(handler))
 .layer(
     CsrfLayer::new()

     // Optionally, configure the layer with the following options:

     // Default: RegenerateToken::PerSession
     .regenerate(RegenerateToken::PerUse)
     // Default: "X-CSRF-TOKEN"
     .request_header("X-Custom-Request-Header")
     // Default: "X-CSRF-TOKEN"
     .response_header("X-Custom-Response-Header")
     // Default: "_csrf_token"
     .session_key("_custom_session_key")
 )
 .layer(SessionLayer::new(MemoryStore::new(), &secret));

// Use hyper to run `app` as service and expose on a local port or socket.

Receive the token and send same-site requests, using your custom header:

const test = async () => {
  // Receive CSRF token (Default response header name: 'X-CSRF-TOKEN')
  const token = (await fetch("/")).headers.get("X-Custom-Response-Header");

  // Submit data using the token
  await fetch("/", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      // Default request header name: 'X-CSRF-TOKEN'
      "X-Custom-Request-Header": token,
    },
    body: JSON.stringify({
      /* ... */
    }),
  });
};

For a full demo, run the same-site example project. You will find the interactive demo at http://127.0.0.1:3000.

CORS-enabled usage

Note: The crate repository contains example projects for same-site and cross-site usage! In each example directory, execute cargo run, then open http://127.0.0.1:3000 in your browser.

Configure your CORS layer, session and CSRF protection layer in your backend application:

use axum::{
    body::Body,
    http::{header, Method, StatusCode},
    routing::{get, Router},
};
use axum_csrf_sync_pattern::{CsrfLayer, RegenerateToken};
use axum_sessions::{async_session::MemoryStore, SessionLayer};
use rand::RngCore;
use tower_http::cors::{AllowOrigin, CorsLayer};

let mut secret = [0; 64];
rand::thread_rng().try_fill_bytes(&mut secret).unwrap();

async fn handler() -> StatusCode {
    StatusCode::OK
}

let app = Router::new()
 .route("/", get(handler).post(handler))
 .layer(
     // See example above for custom layer configuration.
     CsrfLayer::new()
 )
 .layer(SessionLayer::new(MemoryStore::new(), &secret))
 .layer(
     CorsLayer::new()
         .allow_origin(AllowOrigin::list(["https://www.example.com".parse().unwrap()]))
         .allow_methods([Method::GET, Method::POST])
         .allow_headers([header::CONTENT_TYPE, "X-CSRF-TOKEN".parse().unwrap()])
         .allow_credentials(true)
         .expose_headers(["X-CSRF-TOKEN".parse().unwrap()]),
);

// Use hyper to run `app` as service and expose on a local port or socket.

Receive the token and send cross-site requests, using your custom header:

const test = async () => {
  // Receive CSRF token
  const token = (
    await fetch("https://backend.example.com/", {
      credentials: "include",
    })
  ).headers.get("X-CSRF-TOKEN");

  // Submit data using the token
  await fetch("https://backend.example.com/", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "X-CSRF-TOKEN": token,
    },
    credentials: "include",
    body: JSON.stringify({
      /* ... */
    }),
  });
};

For a full demo, run the cross-site example project. You will find the interactive demo at http://127.0.0.1:3000.

Contributing

Pull requests are welcome!

Comments
  • Update for axum 0.6

    Update for axum 0.6

    I'm sure you're already aware but either way may be useful to have a tracking issue for this.

    Changelog including breaking changes available here.

    Thank you for this crate!

    opened by blaenk 2
  • Add quality restraints

    Add quality restraints

    #![forbid(unsafe_code, future_incompatible)]
    #![deny(
        missing_debug_implementations,
        nonstandard_style,
        missing_docs,
        unreachable_pub,
        missing_copy_implementations,
        unused_qualifications
    )]
    
    
    opened by LeoniePhiline 0
  • Add benchmarks and automate them

    Add benchmarks and automate them

    • Benchmark with vs without middleware
    • Observe performance changes per commit / PR
    • Use benchmark results for deeper considerations regarding #1

    See also: https://seenaburns.com/benchmarking-rust-with-cargo-bench/

    opened by LeoniePhiline 0
  • Add `dangerously_pass_through_form_requests` to support extractor-based classical forms?

    Add `dangerously_pass_through_form_requests` to support extractor-based classical forms?

    Library users will be responsible for making sure CSRF checks are performed from form data. (Consuming the body is necessary for classic, formdata-based CSRF protection.)

    opened by LeoniePhiline 1
  • Set up CI for testing, code coverage, benchmarks and dependency updates / semver releases

    Set up CI for testing, code coverage, benchmarks and dependency updates / semver releases

    • ✅ Set up GitHub actions ➞ Fixed by 718aa8dba2df5e2cf19520fb7306854e24414d35
      • cargo test
      • cargo clippy with warnings
      • cargo fmt check
      • ✅ Do not forget to add a CI badge
    • Code coverage
      • https://github.com/marketplace/actions/codecov
      • https://app.codecov.io/login/gh
      • See also https://blog.rng0.io/how-to-do-code-coverage-in-rust
    • Semver releases
      • See also https://github.com/crate-ci/cargo-release
    • Benchmarks (see #18)
    opened by LeoniePhiline 0
  • Is it worth holding the write lock less long?

    Is it worth holding the write lock less long?

    Investigate how axum-sessions' SessionHandle interacts with async-session, and how concurrent requests using the same session interact with the underlying session store.

    Is the session data exclusive to a request? Is it shared between concurrent requests? How can we get the latest data? When is the underlying store written and read?

    Minimally, after the inner service has responded, the token should be re-read from the session if session data is shared between requests. Returning the latest possible token is especially relevant in PerRequest token regeneration mode.

    opened by LeoniePhiline 0
Releases(0.2.2)
  • 0.2.2(Dec 1, 2022)

    Fixed

    • Re-release - forgot to update version in Cargo.toml for 0.2.1.
    • Fixed punctuation in CHANGELOG.md. (68df15d)

    Full Changelog: https://github.com/LeoniePhiline/axum-csrf-sync-pattern/compare/0.2.1...0.2.2

    Source code(tar.gz)
    Source code(zip)
  • 0.2.1(Dec 1, 2022)

  • 0.2.0(Nov 29, 2022)

    Added

    Changed

    • Shortened middleware and layer names to CsrfMiddleware and CsrfLayer for improved DX and more elegant code.

      Migration: If you prefer to keep on using the old name(s) in your code base, the import them with an alias:

      use axum_csrf_sync_pattern::CsrfLayer as CsrfSynchronizerTokenLayer;
      
      // If you import the middleware:
      use axum_csrf_sync_pattern::CsrfMiddleware as CsrfSynchronizerTokenMiddleware;
      
    • Re-licensed the project under Mozilla Public License 2.0, allowing for commercial use, while inciting contributions.

    • Updated tokio from 1.21 to tokio 1.22.

    Removed

    • Removed support for axum 0.5, axum-core 0.2 and axum-sessions 0.3.

    Full Changelog: https://github.com/LeoniePhiline/axum-csrf-sync-pattern/compare/0.1.4...0.2.0

    Source code(tar.gz)
    Source code(zip)
  • 0.1.4(Nov 29, 2022)

    Added

    • Tested code coverage and added tests covering the error path.
    • Added Cargo.toml snippet for quick-installation to README.md.
    • Added CsrfSynchronizerTokenMiddleware::layer() for the sake of convention.
    • Added CsrfSynchronizerTokenLayer::new() for the sake of convention.
    • Now depending on the more stable axum-core where possible.
    • Now indicating project state with badges.
    • Added a CHANGELOG.md.

    Changed

    • Rewrote example / demo projects to never panic, but use appropriate error handling instead.
    • Removed direct dependency on async-session, using the re-export from axum-sessions instead.

    Full Changelog: https://github.com/LeoniePhiline/axum-csrf-sync-pattern/compare/0.1.3...0.1.4

    Source code(tar.gz)
    Source code(zip)
  • 0.1.3(Nov 29, 2022)

    Fixed

    • Properly linked demo URL to help users find the frontend address after cargo run.

    Full Changelog: https://github.com/LeoniePhiline/axum-csrf-sync-pattern/compare/0.1.2...0.1.3

    Source code(tar.gz)
    Source code(zip)
  • 0.1.2(Nov 29, 2022)

  • 0.1.1(Nov 29, 2022)

  • 0.1.0(Nov 29, 2022)

    Added

    • Implemented CSRF Synchronizer Token Middleware and Layer.
    • Example / demo projects for same-site and cross-site usage.
    • Added full crate documentation.

    Full Changelog: https://github.com/LeoniePhiline/axum-csrf-sync-pattern/releases/tag/0.1.0

    Source code(tar.gz)
    Source code(zip)
Owner
Rustacean from Berlin, Germany. Mostly working the the web field, with a focus on security and performance. Quadrocopter enthusiast.
null
Discover GitHub token scope permission and return you an easy interface for checking token permission before querying GitHub.

github-scopes-rs Discover GitHub token scope permission and return you an easy interface for checking token permission before querying GitHub. In many

null 8 Sep 15, 2022
axum-serde is a library that provides multiple serde-based extractors and responders for the Axum web framework.

axum-serde ?? Overview axum-serde is a library that provides multiple serde-based extractors / responses for the Axum web framework. It also offers a

GengTeng 3 Dec 12, 2023
A cookie manager middleware built on top of tower.

tower-cookies A cookie manager middleware built on top of tower. Example With axum: use axum::{handler::get, Router}; use std::net::SocketAddr; use to

Imbolc 47 Dec 9, 2022
A highly customizable, full scale web backend for web-rwkv, built on axum with websocket protocol.

web-rwkv-axum A axum web backend for web-rwkv, built on websocket. Supports BNF-constrained grammar, CFG sampling, etc., all streamed over network. St

Li Junyu 12 Sep 25, 2023
Rate Limiting middleware for Tower/Axum/Tonic/Hyper utilizing the governor crate

A Tower service and layer that provides a rate-limiting backend by governor. Based heavily on the work done for actix-governor. Works with Axum, Hyper

Ben Wishovich 31 Feb 15, 2023
Rust Rocket MongoDB token-authorization REST API boilerplate

Rust Rocket MongoDB token-auth REST API boilerplate In this repository, you can find backend Rust rocket mongodb rest-api boilerplate with token autho

null 6 Dec 7, 2022
Experiments with Rust CRDTs using Tokio web application framework Axum.

crdt-genome Synopsis Experiments with Rust CRDTs using Tokio web application framework Axum. Background Exploring some ideas of Martin Kleppmann, part

dougfort 3 Mar 18, 2022
Axum web framework tutorial for beginners.

Axum Tutorial For Beginners Hello web developers! This tutorial will cover how to write simple web applications in rust with axum framework. If you ar

Eray Karatay 46 Jan 5, 2023
Yew + Axum + blog = Yab

Yew + Axum + blog = Yab

STUDIO RSBM 13 Dec 5, 2022
Demo of Rust and axum web framework

Demo of Rust and axum web framework Demonstration of: Rust: programming language that focuses on reliability and stability. axum: web framework that f

Joel Parker Henderson 115 Dec 29, 2022
Rust Axum+SQLx Sample

rust-axum-sqlx-sample Install git clone https://github.com/web3ten0/rust-axum-sqlx-1.git cd rust-axum-sqlx-1/local docker-compose up -d sh scripts/exe

web3ten0 5 Jul 9, 2022
🪪 Session-based user authentication for Axum.

axum-login ?? Session-based user authentication for Axum. ?? Overview axum-login is a Tower middleware providing session-based user authentication for

Max Countryman 99 Jan 5, 2023
Layers, extractors and template engine wrappers for axum based Web MVC applications

axum-template Layers, extractors and template engine wrappers for axum based Web MVC applications Getting started Cargo.toml [dependencies] axum-templ

Altair Bueno 11 Dec 15, 2022
🔎 Prometheus metrics middleware for Axum

Axum-Prometheus A Prometheus middleware to collect HTTP metrics for Axum applications. axum-prometheus relies on metrics_exporter_prometheus as a back

Péter Leéh 14 Jan 4, 2023
Provides json/csv/protobuf streaming support for axum

axum streams for Rust Library provides HTTP response streaming support for axum web framework: JSON array stream format JSON lines stream format CSV s

Abdulla Abdurakhmanov 15 Dec 11, 2022
Simple example of axum, sqlx with sqlite and utoipa (swagger) - without auth

axum_crud_api Simple example to learn creating CRUD rest apis in Rust with axum, sqlx with sqlite and utoipa (swagger) - without auth Also shows how t

null 2 Nov 12, 2022
Heavy Metal Leptos Stack with Tailwind, Axum, Sqlite, and Cargo Leptos

Heavy Metal Stack Leptos stack with Axum, TailwindCSS, and Sqlite This example creates a basic todo app with an Axum backend that uses Leptos' server

Ben Wishovich 7 Dec 31, 2022
An adapter to easily allow an Axum server to be run within a Cloudflare worker.

axum-cloudflare-adapter An adapter to easily allow an Axum server to be run within a Cloudflare worker. Usage use worker::*; use axum::{ response

Logan Keenan 4 Feb 28, 2023
Starter template for use with the Leptos web framework and Axum.

Leptos Axum Starter Template This is a template for use with the Leptos web framework and the cargo-leptos tool using Axum. Creating your template rep

Leptos 10 Mar 4, 2023