Regex Dos in Axum with Basic Auth
Regex Dos in Axum with Basic Auth — how this specific combination creates or exposes the vulnerability
Axum applications that use HTTP Basic Authentication and rely on regular expressions to validate or extract credentials can be exposed to ReDoS (Regular Expression Denial of Service) when patterns are not carefully constrained. ReDoS occurs when a crafted input causes catastrophic backtracking, consuming excessive CPU time and making the service unresponsive. This section explains how Axum route design, extractor usage, and regex-heavy validation intersect with Basic Auth to create exploitable conditions.
Basic Authentication conveys credentials in the Authorization header as Basic base64(username:password). In Axum, handlers often decode this header and apply custom logic or regex to validate format, permitted characters, or structure. For example, a developer might use a regex to ensure usernames and passwords contain only a restricted character set, or to parse legacy formats embedded in credentials. When these regexes are applied to attacker-controlled input (the decoded username or password), problematic patterns—such as nested quantifiers or ambiguous groupings—can be triggered by specially crafted strings.
Consider a route that decodes the Basic Auth header and validates credentials with a permissive character class and a quantifier that overlaps. A pattern like ^[a-zA-Z0-9_]*:.*$ applied to the decoded payload can exhibit exponential backtracking when the input contains long sequences of characters that satisfy [a-zA-Z0-9_]* while the engine attempts many ways to satisfy the ambiguous .* that follows. In Axum, this typically occurs inside extractor implementations or guard closures that synchronously evaluate regexes on each request. Because scans performed by middleBrick include checks for Input Validation and Unsafe Consumption, such patterns can be surfaced as high-severity findings when the runtime behavior indicates a risk of resource exhaustion.
Another common scenario involves repeated optional groups or nested quantifiers in patterns used to enforce policy (for example, matching a username with optional segments like ^(?:user|admin|guest)(?:-v\d+)?$ combined with permissive suffix matching). When combined with Basic Auth, an attacker can send credentials designed to maximize backtracking paths, especially if the regex is applied repeatedly per request or within middleware that processes many headers. Because middleBrick’s checks include Input Validation and Rate Limiting, findings may highlight both the regex risk and the absence of adequate request throttling, which together amplify the availability impact.
To illustrate the mechanics, a vulnerable Axum extractor might look like the following Rust code. Note that this pattern is intentionally fragile to demonstrate the issue; the quantifier ambiguity and open-ended character classes create conditions suitable for ReDoS when the decoded credential is controlled by an attacker.
use axum::{{
async_trait,
extract::{FromRequest, Request},
http::request::Parts,
}};
use regex::Regex;
use std::convert::Infallible;
struct BasicCredentials { pub username: String, pub password: String }
#[async_trait]
impl FromRequest<S> for BasicCredentials
where
S: Send + Sync,
{
type Rejection = Infallible;
async fn from_request(req: Request, _state: &S) -> Result<Self, Self::Rejection> {
let header = req.headers().get("authorization")
.and_then(|v| v.to_str().ok())
.unwrap_or("");
if let Some(creds) = header.strip_prefix("Basic ") {
if let Ok(decoded) = base64::decode(creds) {
if let Ok(pair) = String::from_utf8(decoded) {
// Vulnerable regex: overlapping quantifiers and permissive character class
let re = Regex::new(r"^([a-zA-Z0-9_]*):(.*)$").ok();
if let Some(r) = re {
if let Some(caps) = r.captures(&pair) {
let _user = caps.get(1).map(|m| m.as_str().to_string());
let _pass = caps.get(2).map(|m| m.as_str().to_string());
}
}
}
}
}
// Fallback to default rejection handling in real code
Result::Ok(BasicCredentials { username: String::new(), password: String::new() })
}
}
In this example, an attacker can supply a long string of alphanumerics before the colon to force extensive backtracking in the [a-zA-Z0-9_]* group while simultaneously exploring many ways for .* to consume the remainder. middleBrick’s findings would typically recommend anchoring patterns, avoiding ambiguous quantifiers, and using length or prefix checks instead of permissive regexes where possible.
Basic Auth-Specific Remediation in Axum — concrete code fixes
Remediation focuses on removing or minimizing regex use for credential validation and replacing it with bounded, deterministic checks. When Basic Auth is required in Axum, prefer strict prefix stripping, base64 decoding, and simple length or character-set checks implemented with iterators or small finite-state logic rather than complex patterns. If regex is necessary, ensure quantifiers are non-overlapping and bounded, and avoid ambiguous groupings.
Below is a revised Axum extractor that avoids problematic regex entirely. It decodes the Basic Auth header, validates length bounds, and ensures username and password contain only permitted characters using a constant-time predicate check. This approach eliminates catastrophic backtracking and aligns with security guidance highlighted by middleBrick’s findings on Input Validation and Unsafe Consumption.
use axum::{
async_trait,
extract::{FromRequest, Request},
http::request::Parts,
};
use base64::Engine;
use std::convert::Infallible;
struct BasicCredentials { pub username: String, pub password: String }
const ALLOWED_USER_CHARS: &[u8] = b"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_.-";
const ALLOWED_PASS_CHARS: &[u8] = b"!"#\$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~";
const MAX_LEN: usize = 256;
fn is_allowed(bytes: &[u8], allowed: &[u8]) -> bool {
bytes.iter().all(|b| allowed.contains(b))
}
#[async_trait]
impl<S> FromRequest<S> for BasicCredentials
where
S: Send + Sync,
{
type Rejection = Infallible;
async fn from_request(req: Request, _state: &S) -> Result<Self, Self::Rejection> {
let header = req.headers().get("authorization")
.and_then(|v| v.to_str().ok())
.unwrap_or("");
let mut username = String::new();
let mut password = String::new();
if let Some(stripped) = header.strip_prefix("Basic ") {
if let Ok(decoded) = base64::engine::general_purpose::STANDARD.decode(stripped) {
// Split at first colon only to avoid scanning entire payload repeatedly
if let Some(idx) = decoded.iter().position(|&b| b == b':') {
if decoded.len() > MAX_LEN {
return Result::Ok(BasicCredentials { username, password });
}
let (user_bytes, pass_bytes) = decoded.split_at(idx);
let pass_bytes = &pass_bytes[1..]; // skip colon
if is_allowed(user_bytes, ALLOWED_USER_CHARS) && is_allowed(pass_bytes, ALLOWED_PASS_CHARS) {
username = String::from_utf8_lossy(user_bytes).to_string();
password = String::from_utf8_lossy(pass_bytes).to_string();
}
}
}
}
Result::Ok(BasicCredentials { username, password })
}
}
For projects that still require regex-based validation (for example, to enforce specific username formats), use bounded quantifiers and possessive quantifiers where the regex engine supports them, and avoid patterns with nested quantifiers over overlapping character classes. In Rust, prefer the regex crate with the regex-automata backend configured for non-backtracking matching when possible. Additionally, enforce global rate limiting and request timeouts at the Axum middleware layer to mitigate any residual availability risk, a practice often highlighted in middleBrick’s Pro plan continuous monitoring and CI/CD integration guidance.
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 |