Denial Of Service in Axum with Mutual Tls
Denial Of Service in Axum with Mutual Tls — how this specific combination creates or exposes the vulnerability
Mutual Transport Layer Security (mTLS) in Axum adds a strong identity guarantee by requiring both the client and server to present valid certificates. While mTLS reduces risk from unauthorized clients, it can introduce Denial of Service (DoS) conditions when the handshake or certificate validation logic interacts poorly with request handling, resource limits, or runtime behavior.
In Axum, mTLS is typically enforced via a TLS acceptor (such as with tower-rs/rustls or axum::Server::bind_rustls) that completes the handshake before requests are handed to the application. If certificate validation is synchronous, CPU-intensive, or performs blocking I/O (for example, checking revocation via OCSP or doing online certificate lookup), the TLS acceptor thread can become a bottleneck. Under sustained connection attempts, this can exhaust worker threads or the bounded connection queue, causing legitimate requests to time out or be refused.
Additionally, mTLS enriches each request with peer identity (often mapped into extensions for downstream use). If the application or middleware eagerly validates certificates beyond the handshake (for example, re-checking revocation for every request) or performs expensive operations on the certificate data per request, the per-request cost increases. Combined with a high rate of short-lived connections (a common DoS pattern), this can saturate CPU and memory, leading to service unavailability for benign clients.
Another vector specific to mTLS is the size and complexity of the certificate chain. Large chains or many intermediate CAs increase handshake latency and memory use per connection. If Axum or the underlying acceptor does not enforce sensible limits on certificate chain size or header counts, an attacker can craft connections with oversized certificates to consume memory and trigger DoS. Rate limiting applied only at the HTTP layer may be bypassed if the resource exhaustion occurs earlier in the TLS handshake or connection acceptance stage.
Mutual Tls-Specific Remediation in Axum — concrete code fixes
Remediation focuses on reducing handshake cost, bounding resource usage, and ensuring validation does not block the request path. Prefer asynchronous, non-blocking certificate validation and avoid per-request expensive checks. Use connection and rate limits early, and enforce sensible certificate constraints.
Example: Configure Axum with rustls in non-blocking mode and set certificate and connection limits.
use axum::Router;
use rustls::{Certificate, PrivateKey, ServerConfig};
use rustls_pemfile::{certs, pkcs8_private_keys};
use std::fs::File;
use std::io::BufReader;
use std::net::SocketAddr;
use tokio::net::TcpListener;
use tower_http::trace::TraceLayer;
async fn build_server() -> Result<Router, Box<dyn std::error::Error + Send + Sync>> {
// Load cert and key
let cert_file = &mut BufReader::new(File::open("cert.pem")?);
let key_file = &mut BufReader::new(File::open("key.pem")?);
let cert_chain: Vec<Certificate> = certs(cert_file)?.into_iter().map(Certificate).collect();
let mut keys: Vec<PrivateKey> = pkcs8_private_keys(key_file)?.into_iter().map(PrivateKey).collect();
// Configure rustls with safe options
let mut config = ServerConfig::builder()
.with_safe_defaults()
.with_client_cert_verifier(std::sync::Arc::new(
// Example: a simple verifier that checks the cert is issued by a known CA
rustls::server::AllowAnyAuthenticatedClient::new(vec![/* trusted CA certs */]),
))
.with_single_cert(cert_chain, keys.remove(0))?;
// Limit session tickets and buffer sizes to reduce DoS surface
config.session_storage = std::sync::Arc::new(rustls::server::ServerSessionStorage::default());
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
let listener = TcpListener::bind(&addr).await?;
let app = Router::new()
.route("/", axum::routing::get(|| async { "ok" }))
.layer(TraceLayer::new_for_http());
axum::Server::from_tcp_rustls(listener, config)
.unwrap()
.serve(app.into_make_service())
.await?
.map_err(Into::into)
}
Key practices:
- Use asynchronous, non-blocking certificate verification to avoid tying up worker threads.
- Set limits on certificate chain depth and size at the TLS layer to prevent memory exhaustion from oversized chains.
- Apply rate limiting and connection caps early (e.g., in the TCP listener or acceptor) to stop floods before they consume expensive handshake resources.
- Cache validation results where safe (for example, short-lived OCSP stapled responses) to avoid repeated remote calls per request.
- Monitor TLS handshake duration and connection counts to detect abnormal patterns that may indicate an active DoS attempt.
Related CWEs: resourceConsumption
| CWE ID | Name | Severity |
|---|---|---|
| CWE-400 | Uncontrolled Resource Consumption | HIGH |
| CWE-770 | Allocation of Resources Without Limits | MEDIUM |
| CWE-799 | Improper Control of Interaction Frequency | MEDIUM |
| CWE-835 | Infinite Loop | HIGH |
| CWE-1050 | Excessive Platform Resource Consumption | MEDIUM |