Man In The Middle in Axum with Mutual Tls
Man In The Middle in Axum with Mutual Tls — how this specific combination creates or exposes the vulnerability
A Man In The Middle (MitM) attack against an Axum service using Mutual TLS (mTLS) can occur when certificate validation is incomplete or when the application does not enforce server-side verification of client certificates. Even when both parties present certificates, an attacker positioned on the network can attempt to intercept traffic if the server does not properly validate the client cert chain, hostname, or revocation status. Axum’s HTTPS layer relies on the underlying hyper::client::connect::HttpsConnector (or tower::https::HttpsConnector) and the rustls configuration provided by the application. If the server’s TLS acceptor is configured with a permissive client authentication mode (e.g., RequestClientCert) instead of RequireClientCert, or if it skips hostname verification, an attacker can present a valid but stolen CA-signed certificate and relay requests while viewing or altering traffic.
In practice, a misconfigured mTLS setup in Axum may expose endpoints to classic TLS downgrade or certificate substitution if the server does not enforce strong cipher suites or disable insecure protocol versions. Since Axum does not perform additional application-layer identity checks beyond the TLS layer, the server must ensure that the client certificate contains the expected Extended Key Usage or a specific subject field. Without these checks, an attacker who obtains a client certificate (for example, via insecure storage or a compromised CA) can position themselves between the client and the Axum server, forwarding requests and responses while remaining undetected if logging and certificate transparency checks are not implemented.
Another subtle vector involves the use of HTTP/2 or HTTP/1.1 with keep-alive connections where the server reuses a verified TLS session without re-verifying client identity on subsequent requests. If the mTLS configuration does not bind certificate metadata to the application session, an attacker might hijack an existing authorized connection. This is especially relevant when Axum services are deployed behind load balancers or reverse proxies that terminate TLS and forward unencrypted traffic internally; if the internal segment does not enforce mTLS, the effective security boundary is reduced.
To detect such risks using tools like middleBrick, you can scan your unauthenticated attack surface to observe whether the server requests but does not strictly require client certificates, and whether the TLS configuration exposes weak parameters. middleBrick runs 12 security checks in parallel, including Authentication and SSL/TLS Configuration, to surface findings such as missing client cert enforcement and certificate validation gaps, along with prioritized remediation guidance mapped to frameworks like OWASP API Top 10 and PCI-DSS.
Mutual Tls-Specific Remediation in Axum — concrete code fixes
Remediation centers on enforcing strict client certificate validation, tightening TLS parameters, and binding certificate identity to the application session. In Axum, this is typically done by configuring rustls on the server side and ensuring the client also validates the server certificate chain and hostname. Below are concrete, working examples that demonstrate a hardened mTLS setup.
Server-side: Require and verify client certificates
The server must be configured with a root CA that signs client certificates and must require valid client certificates. Use rustls::ServerConfig with client_auth_mandatory set to true and provide a verifier that checks the certificate chain against your trusted CA.
use axum::Server;
use rustls::{Certificate, PrivateKey, ServerConfig};
use rustls::pki_types::{CertificateDer, PrivateKeyDer, ServerName};
use std::net::SocketAddr;
use std::sync::Arc;
async fn make_server_config() -> Arc {
// Load server cert and key
let cert_chain = vec![CertificateDer::from(include_bytes("server.crt").to_vec())];
let private_key = PrivateKeyDer::from(include_bytes("server.key").to_vec());
// Load trusted client CA for verifying client certificates
let client_ca_cert = CertificateDer::from(include_bytes("client-ca.crt").to_vec());
let mut root_store = rustls::RootCertStore::empty();
root_store.add(client_ca_cert).expect("invalid client CA");
let client_auth = rustls::server::ClientAuthMandatory::Required(Arc::new(root_store));
let config = ServerConfig::builder()
.with_safe_defaults()
.with_client_auth_mandatory(client_auth)
.with_single_cert(cert_chain, private_key)
.expect("invalid server certificate or key");
Arc::new(config)
}
#[tokio::main]
async fn main() {
let config = make_server_config().await;
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
let app = axum::Router::new()
.route("/", axum::routing::get(|| async { "Hello mTLS" }));
Server::bind(&addr)
.https(hyper_rustls::TlsServer::new(config))
.serve(app.into_make_service())
.await
.unwrap();
}
Client-side: Validate server certificate and hostname
The client should load the server CA and enable hostname verification to prevent connecting to a malicious server presenting a valid but wrong certificate.
use reqwest::Client;
use rustls::{ClientConfig, RootCertStore};
use rustls::pki_types::CertificateDer;
use std::sync::Arc;
async fn build_mtls_client() -> Client {
// Load trusted server CA
let server_ca = CertificateDer::from(include_bytes("server-ca.crt").to_vec());
let mut root_store = RootCertStore::empty();
root_store.add(server_ca).expect("invalid server CA");
let client_config = ClientConfig::builder()
.with_root_certificates(root_store)
.with_no_client_auth(); // client auth handled by server; client presents cert optionally
let tls_connector = std::sync::Arc::new(HyperTls::new());
let client = Client::builder()
.use_preconfigured_tls(client_config)
.build()
.unwrap();
client
}
Additional hardening steps
- Prefer strong cipher suites and disable legacy protocols by using
with_safe_defaults()and explicitly settingversionsto TLSv1.2 and TLSv1.3 only. - Validate extended key usage and subject fields in client certificates within your application logic to ensure the presented identity matches expectations.
- Enable revocation checks (OCSP/CRL) via a custom verifier if your PKI infrastructure supports it; rustls supports custom server verifiers for this purpose.
- Ensure that any reverse proxy or load balancer also enforces mTLS and does not forward unverified requests to the Axum backend.
By combining these code-level controls with periodic scans via middleBrick, you can verify that your Axum endpoints enforce mTLS correctly and reduce the window for effective MitM attacks.