Skip to main content

doido_controller/
csrf.rs

1//! CSRF protection via the double-submit-cookie pattern.
2//!
3//! A random token is placed in a `csrf_token` cookie and echoed back by the
4//! client in the `X-CSRF-Token` header (or a form field). The [`with_csrf`]
5//! middleware (see [`crate::stack::MiddlewareStack`]) requires the two to match
6//! on state-changing requests, so a cross-site request — which cannot read the
7//! cookie to echo it — is rejected.
8
9use subtle::ConstantTimeEq;
10
11/// Mint a fresh, unguessable CSRF token (256 bits of randomness, hex-encoded).
12pub fn generate_token() -> String {
13    format!(
14        "{}{}",
15        uuid::Uuid::new_v4().simple(),
16        uuid::Uuid::new_v4().simple()
17    )
18}
19
20/// Constant-time comparison of a stored token and a submitted one.
21pub fn tokens_match(expected: &str, actual: &str) -> bool {
22    expected.as_bytes().ct_eq(actual.as_bytes()).into()
23}
24
25/// Extract the `csrf_token` value from a `Cookie` header, if present.
26pub(crate) fn token_from_cookie_header(cookie_header: &str) -> Option<String> {
27    cookie_header
28        .split(';')
29        .filter_map(|pair| pair.trim().split_once('='))
30        .find(|(name, _)| *name == "csrf_token")
31        .map(|(_, value)| value.to_string())
32}