Missing Authentication in Axum with Mutual Tls
Missing Authentication in Axum with Mutual Tls — how this specific combination creates or exposes the vulnerability
Mutual Transport Layer Security (mTLS) requires both the client and the server to present valid certificates during the TLS handshake. In Axum, enabling mTLS typically involves configuring a Rust TLS acceptor that requests and validates client certificates. When authentication is missing or misconfigured in this setup, the API may accept requests without verifying the client certificate, effectively bypassing the intended mutual authentication.
Consider an Axum service built with tower-rs and rustls. If the server is configured to request client certificates but does not enforce validation — for example, by not setting a proper ClientCertVerified policy or by failing to inspect the certificate chain — an attacker can send requests without providing a certificate or with an invalid one. The server may still process these requests as authenticated, leading to unauthorized access to endpoints that should be protected.
This misconfiguration is particularly dangerous because it can expose an otherwise secure protocol setup. The server logs may indicate successful TLS handshakes, while in reality, the client identity was never verified. Attackers can exploit this by targeting unauthenticated routes or leveraging weak endpoint-level authorization (BOLA/IDOR) to access or modify other users' data. The vulnerability aligns with the OWASP API Top 10 category of Broken Object Level Authorization, where missing or bypassed authentication enables unauthorized operations.
During a middleBrick scan, such a configuration might result in a failing Authentication check, even when mTLS is enabled. The scanner tests whether the endpoint enforces client identity and whether unauthenticated requests are accepted. Findings will include a high-severity Authentication issue with remediation guidance to tighten certificate validation.
Real-world examples include services that use rustls::ServerConfig with NoClientAuth accidentally left in production code, or setups where the certificate verification callback always returns Ok without inspecting the certificate contents. These patterns nullify the protection that mTLS is designed to provide.
Mutual Tls-Specific Remediation in Axum — concrete code fixes
To properly secure an Axum service with mTLS, you must enforce client certificate validation at the TLS layer and ensure that the authenticated client identity is propagated into your application state or request extensions.
Below is a syntactically correct example of configuring Axum with mutual TLS using rustls and axum::Server. This setup ensures that the server requests and validates client certificates, and rejects connections that do not provide valid ones:
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 std::sync::Arc;
async fn load_rustls_config() -> Arc<ServerConfig> {
// Load server certificate and private key
let cert_file = &mut BufReader::new(File::open("server.crt").unwrap());
let key_file = &mut BufReader::new(File::open("server.key").unwrap());
let cert_chain = certs(cert_file)
.collect<Result<Vec<_>, _>>()
.unwrap()
.into_iter()
.map(Certificate)
.collect();
let mut keys = pkcs8_private_keys(key_file)
.collect<Result<Vec<_>, _>>()
.unwrap()
.into_iter()
.map(PrivateKey)
.collect();
// Configure client authentication
let mut config = ServerConfig::builder()
.with_safe_defaults()
.with_no_client_auth() // Start with no auth, then customize
.with_single_cert(cert_chain, keys.remove(0))
.unwrap();
// Enforce client certificate verification
config.client_auth_root_subjects = vec!["Example CA".parse().unwrap()];
config.client_auth_mandatory = true;
Arc::new(config)
}
#[tokio::main]
async fn main() {
let app = Router::new().into_make_service();
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
let tls_config = load_rustls_config().await;
axum::Server::bind(&addr)
.tls_config(tls_config)
.unwrap()
.serve(app)
.await
.unwrap();
}
In this example, client_auth_mandatory is set to true, which requires clients to present a certificate signed by a trusted CA. The client_auth_root_subjects further restricts acceptable issuers. This configuration ensures that unauthenticated requests are rejected at the TLS layer before reaching Axum routes.
For production use, integrate this with your application state to extract principal information from the client certificate. You can use Axum’s extension or request extensions to pass verified identity into your handlers, enabling proper authorization checks while preventing BOLA/IDOR issues.
Using the middleBrick CLI (middlebrick scan <url>) or the GitHub Action helps detect missing authentication even when mTLS appears enabled. The scanner validates that client verification is enforced and flags configurations where certificates are requested but not validated.
Related CWEs: authentication
| CWE ID | Name | Severity |
|---|---|---|
| CWE-287 | Improper Authentication | CRITICAL |
| CWE-306 | Missing Authentication for Critical Function | CRITICAL |
| CWE-307 | Brute Force | HIGH |
| CWE-308 | Single-Factor Authentication | MEDIUM |
| CWE-309 | Use of Password System for Primary Authentication | MEDIUM |
| CWE-347 | Improper Verification of Cryptographic Signature | HIGH |
| CWE-384 | Session Fixation | HIGH |
| CWE-521 | Weak Password Requirements | MEDIUM |
| CWE-613 | Insufficient Session Expiration | MEDIUM |
| CWE-640 | Weak Password Recovery | HIGH |