Regex Dos in Axum with Jwt Tokens
Regex Dos in Axum with Jwt Tokens — how this specific combination creates or exposes the vulnerability
A Regular Expression Denial of Service (Regex DoS) occurs when a regex pattern exhibits catastrophic backtracking on untrusted input, causing CPU usage to spike and blocking the event loop. In Axum, this risk is heightened when JWT token validation uses complex or poorly constructed patterns—such as repeated quantifiers on large strings—within middleware or handlers. If a route parses and validates JWTs using vulnerable regex (for instance, checking claims or the token structure with non-anchored, greedy patterns), an attacker can craft a long, specially designed token that forces exponential backtracking. Because Axum applications often process authentication eagerly, a single malicious request can consume significant server resources, degrading performance for all users.
JWT tokens are typically Base64Url-encoded strings with three parts separated by dots (header.payload.signature). When regex is used to validate token format, claims, or the presence of specific keys, the pattern must be precise. Patterns that use nested quantifiers (e.g., (a+)+) or overly permissive matching on variable-length segments can be exploited. For example, a regex designed to verify a specific header structure might backtrack extensively on malformed input, especially if the token contains long repetitive sequences. Since Axum does not inherently sanitize JWT strings before regex evaluation, the framework exposes the attack surface at the parsing layer. Attackers can send many such requests to an unauthenticated endpoint, triggering high CPU and potential denial of service. This is particularly relevant when using regex to validate bearer tokens extracted from headers, where the token string is directly passed to the pattern without prior length or format checks.
Jwt Tokens-Specific Remediation in Axum — concrete code fixes
To mitigate Regex DoS in Axum when working with JWT tokens, avoid using regex for structural validation of the token string; instead, rely on dedicated JWT libraries that parse and verify tokens without backtracking-prone operations. If regex is necessary—for example, to validate a specific header format or claim pattern—keep patterns simple, anchored, and non-greedy, and enforce length limits before evaluation. Below are concrete Axum examples showing a vulnerable approach and a secure alternative.
Vulnerable Axum handler using risky regex on JWT tokens:
use axum::{routing::get, Router};use std::net::SocketAddr;use regex::Regex;
async fn auth_handler(header: axum::extract::Header<&str>) -> String { let token = header.to_string(); // Risky: non-anchored, greedy pattern on untrusted token let re = Regex::new(r#"^(ey[A-Za-z0-9_-]+\.){2}ey[A-Za-z0-9_-]+$").unwrap(); // Potential DoS if re.is_match(&token) { "valid".to_string() } else { "invalid".to_string() }}#[tokio::main]async fn main() { let app = Router::new().route("/auth", get(auth_handler)); let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); axum::Server::bind(&addr) .serve(app.into_make_service()) .await .unwrap();}Secure Axum handler with controlled validation:
use axum::{routing::get, Router, extract::Extension, http::HeaderMap};use std::net::SocketAddr;use regex::Regex;use once_cell::sync::Lazy;
// Use a compiled, anchored, and non-backtracking pattern; validate length firststatic SAFE_PATTERN: Lazy<Regex> = Lazy::new(|| { Regex::new(r#"^ey[A-Za-z0-9_-]{10,100}\.[A-Za-z0-9_-]{10,100}\.[A-Za-z0-9_-]{10,100}$").unwrap()});async fn auth_handler(headers: HeaderMap) -> String { // Extract token with a bounded length check to avoid feeding huge input to regex if let Some(token) = headers.get("authorization") { let token_str = token.to_str().unwrap_or(""); if token_str.len() > 500 { return "invalid".to_string(); // reject excessively long tokens } if SAFE_PATTERN.is_match(token_str) { return "valid".to_string(); } } "invalid".to_string()}#[tokio::main]async fn main() { let app = Router::new().route("/auth", get(auth_handler)); let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); axum::Server::bind(&addr) .serve(app.into_make_service()) .await .unwrap();}In the secure version, the regex is anchored, uses bounded quantifiers, and is precompiled with once_cell. The handler first checks token length to prevent feeding large inputs into regex, reducing backtracking risk. For production JWT validation, prefer a library that parses the token structure without regex, ensuring robust security and stability.
Related CWEs: inputValidation
| CWE ID | Name | Severity |
|---|---|---|
| CWE-20 | Improper Input Validation | HIGH |
| CWE-22 | Path Traversal | HIGH |
| CWE-74 | Injection | CRITICAL |
| CWE-77 | Command Injection | CRITICAL |
| CWE-78 | OS Command Injection | CRITICAL |
| CWE-79 | Cross-site Scripting (XSS) | HIGH |
| CWE-89 | SQL Injection | CRITICAL |
| CWE-90 | LDAP Injection | HIGH |
| CWE-91 | XML Injection | HIGH |
| CWE-94 | Code Injection | CRITICAL |