Regex Dos in Axum with Mutual Tls
Regex Dos in Axum with Mutual Tls — how this specific combination creates or exposes the vulnerability
Regex-based denial-of-service (Regex Dos) occurs when an attacker provides input that causes a regular expression to exhibit catastrophic backtracking, consuming excessive CPU time. In Axum, this risk is amplified when mutual TLS is used because the TLS handshake completes before application-level validation runs. With mutual TLS, the server authenticates the client certificate, but the route handler still parses and validates user-controlled inputs such as query parameters, headers, or JSON payloads. If a route uses a non-anchored or overly permissive regex on these inputs, an attacker who has passed mutual TLS can craft malicious payloads that trigger exponential backtracking during routing or extraction, leading to high CPU usage and degraded service for other requests.
Consider an Axum route that extracts an identifier from a path segment using a regex pattern without strict bounds:
use axum::routing::get;
use axum::Router;
use std::net::SocketAddr;
async fn handler(id: axum::extract::Path) -> String {
let re = regex::Regex::new(r\"^(\w+)-(\w+)$\").unwrap(); // non-anchored, repetitive groups
if re.is_match(&id) {
\"ok\".to_string()
} else {
\"invalid\".to_string()
}
}
#[tokio::main]
async fn main() {
let app = Router::new().route("/item/:id", get(handler));
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
axum::Server::bind(&addr)
.serve(app.into_make_service())
.await
.unwrap();
}
The pattern ^(\w+)-(\w+)$ can be problematic with inputs containing repeated characters like a-a due to overlapping word boundaries and backtracking possibilities. When mutual TLS is enforced via middleware (e.g., using tower-https), the request reaches this handler only after client certificate verification, but the regex still operates on attacker-controlled data. An authenticated client can send a path such as /item/a-a with crafted repetitions to trigger long-running backtracking. Because the scan list includes input validation and the LLM/AI Security checks do not cover regex behavior, this issue would surface under the Input Validation category with high severity. The interaction shows that mutual TLS reduces some risks but does not mitigate application-layer regex inefficiencies; it only ensures the peer is authenticated, not that inputs are safe.
Additionally, regex misuse in extractors or guards can interact poorly with Axum extractors that rely on parsing. For example, using a per-request compiled regex inside an extractor without caching or size limits can multiply overhead across concurrent requests. Even though mutual TLS provides transport-layer identity, the server must still validate and limit the complexity of application-level patterns to prevent resource exhaustion. Remediation involves anchoring patterns, avoiding ambiguous nested quantifiers, and precompiling regexes, as detailed in the next section.
Mutual Tls-Specific Remediation in Axum — concrete code fixes
To mitigate Regex Dos in Axum while using mutual TLS, focus on tightening regex patterns and ensuring validation occurs efficiently after TLS authentication. Below are concrete, working examples that combine mutual TLS setup with safe regex usage.
1. Safe regex pattern and precompilation
Define regex patterns with explicit bounds and avoid ambiguous repetitions. Precompile them once at startup to prevent repeated compilation overhead.
use axum::routing::get;
use axum::Router;
use regex::Regex;
use std::net::SocketAddr;
use std::sync::Arc;
struct SafeExtractor(Arc<Regex>);
impl
The pattern ^\w+-\w+\z is anchored and avoids nested quantifiers that cause backtracking. Precompiling the regex outside the handler ensures each request does not re-parse the pattern.
2. Mutual TLS setup with tower-https
Enforce client certificate verification before requests reach regex-sensitive routes. This example uses tower-https to configure mutual TLS in Axum.
use axum::Server;
use tower_http::set_header::SetResponseHeaderLayer;
use tower_https::rustls::RustlsConfig;
use tower_http::trace::TraceLayer;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let tls_config = RustlsConfig::from_pem_file("ca.pem", "server.pem", "server.key", Some("client.pem"))?;
let app = Router::new()
.route("/secure/item/:id", get(handler))
.layer(SetResponseHeaderLayer::new());
let make_svc = tower_http::set_header::SetResponseHeaderLayer::new();
let listener = tokio::net::TcpListener::bind("0.0.0.0:8443").await?;
Server::builder(tls_config.into_make_service_with_connect_info::())
Note: The above snippet illustrates the direction of configuration; in practice, you would integrate tower-https with proper certificate extraction and validation. The key point is that mutual TLS operates at the transport layer, while regex safety must be ensured at the handler or extractor level.
3. Limit input length and complexity
Add middleware or extractor guards to reject overly long or complex inputs before regex processing, reducing the attack surface for Regex Dos even when mutual TLS is in use.
async fn validate_id(id: &str) -> Result<(), axum::http::StatusCode> {
if id.len() > 200 {
return Err(axum::http::StatusCode::PAYLOAD_TOO_LARGE);
}
// Additional checks can be added here
Ok(())
}
By combining anchored regex patterns, precompilation, and transport-layer mutual TLS, you reduce the likelihood of Regex Dos while maintaining strong client authentication.
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 |