HIGH vulnerable componentsaxummutual tls

Vulnerable Components in Axum with Mutual Tls

Vulnerable Components in Axum with Mutual Tls — how this specific combination creates or exposes the vulnerability

Mutual Transport Layer Security (mTLS) in Axum requires both the server and the client to present valid certificates during the TLS handshake. When this is implemented incompletely, several classes of OWASP API Security risks emerge, particularly around authentication bypass and insecure configuration.

A common vulnerable pattern is binding the TLS acceptor to a certificate without enforcing client verification. In Axum, using tower-rs and hyper directly, developers might configure an HTTPS service with a server certificate but omit the client_ca or equivalent verification flags. This creates a scenario where the endpoint appears protected by TLS but accepts unauthenticated clients, undermining the purpose of mTLS.

Another vulnerability arises from improper certificate validation. If the server does not validate the client certificate chain against a trusted Certificate Authority (CA), an attacker can present any certificate and gain access. In Axum, this often occurs when custom TLS acceptor builders are used without setting .danger_accept_invalid_certs to a controlled, secure policy or when relying on default system trust stores without explicitly pinning the expected CA.

Implementation mistakes also expose sensitive information through logging or error messages. For example, logging the subject of a client certificate without redacting sensitive fields can lead to Security Logging and Monitoring Failures. Additionally, failing to set strong cipher suites or using outdated TLS versions (e.g., TLS 1.2 instead of 1.3 where possible) can expose the service to known downgrade attacks or cryptographic weaknesses referenced in real-world CVEs.

When using middleware to inspect client certificates, developers must ensure the extracted identity is used consistently for authorization. A gap here can lead to Broken Object Level Authorization (BOLA) if the subject or claims from the client certificate are not validated against access control rules. middleBrick’s BOLA/IDOR checks can detect such misconfigurations by correlating unauthenticated endpoints with expected authenticated contexts.

Lastly, configuration as code mistakes in deployment pipelines can lead to mismatched certificate deployments, where the server certificate and the trusted client CA bundle are out of sync. This causes handshake failures or, worse, fallback to less secure configurations. middleBrick’s OpenAPI/Swagger analysis, with full $ref resolution, helps cross-reference documented authentication expectations with runtime behavior to highlight inconsistencies.

Mutual Tls-Specific Remediation in Axum — concrete code fixes

To securely implement mTLS in Axum, configure the TLS layer with explicit client verification and strict validation. Below are concrete, working examples using hyper and rustls, which are common in Axum backends.

1. Server with Mandatory Client Certificate Verification

This example ensures the server requests and validates client certificates against a trusted CA.

use axum::{routing::get, Router};
use hyper::server::conn::http1;
use hyper::{service::service_fn, Body, Request, Response};
use std::net::SocketAddr;
use tokio_rustls::rustls::{Certificate, ServerConfig};
use tokio_rustls::TlsAcceptor;
use std::sync::Arc;

async fn handler(_req: Request<Body>) -> Result<Response<Body>, hyper::Error> {
    Ok(Response::new(Body::from("mTLS protected response")))
}

#[tokio::main]
async fn main() {
    // Load server certificate and private key
    let certs = load_certs("server.crt");
    let key = load_private_key("server.key");

    // Configure server-side TLS with client verification
    let mut server_config = ServerConfig::builder()
        .with_safe_defaults()
        .with_no_client_auth() // Start with no auth, then set client CA
        .with_client_cert_verifier(Arc::new(MyClientVerifier::new()));

    server_config.set_single_cert(certs, key).expect("invalid server certificate");

    let tls_acceptor = TlsAcceptor::from(Arc::new(server_config));

    let app = Router::new().route("/", get(handler));
    let service = service_fn(move |req: Request<Body>| {
        let app = &app;
        async move { Ok::<_, hyper::Error>(app.call(req).await?) }
    });

    let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
    let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
    loop {
        let (stream, _) = listener.accept().await.unwrap();
        let acceptor = tls_acceptor.clone();
        tokio::task::spawn(async move {
            let tls_stream = acceptor.accept(stream).await.unwrap();
            http1::Builder::new()
                .serve_connection(tls_stream, service)
                .await
                .unwrap();
        });
    }
}

// Custom verifier that enforces a trusted CA
struct MyClientVerifier {
    ca_cert: Certificate,
}

impl MyClientVerifier {
    fn new() -> Self {
        let ca_cert_pem = std::fs::read("ca.crt").expect("unable to read CA cert");
        let ca_cert = Certificate(ca_cert_pem);
        Self { ca_cert }
    }
}

impl rustls::server::ClientCertVerifier for MyClientVerifier {
    fn offer_client_auth(&self) -> bool { true }
    fn client_auth_mandatory(&self) -> bool { true }
    fn verify_client_cert(
        &self,
        end_entity: &Certificate,
        intermediates: &[Certificate],
        server_name: &rustls::ServerName,
        _scts: &mut dyn Iterator<Item = &[u8]>,
        _ocsp_response: &[u8],
        _now: std::time::SystemTime,
    ) -> Result<rustls::server::ClientCertVerified, rustls::Error> {
        // Validate that the end entity chains up to our CA
        let roots = rustls::RootCertStore::from_vec(vec![self.ca_cert.clone()]);
        let client_auth = rustls::ServerCertVerifier::verify_client_cert(
            &roots,
            end_entity,
            intermediates,
            server_name,
            _scts,
            _ocsp_response,
            _now,
        );
        client_auth
    }
}

fn load_certs(path: &str) -> Vec<Certificate> {
    let certfile = std::fs::File::open(path).expect("cannot open certificate file");
    let mut reader = std::io::BufReader::new(certfile);
    rustls_pemfile::certs(&mut reader)
        .unwrap()
        .into_iter()
        .map(Certificate)
        .collect()
}

fn load_private_key(path: &str) -> rustls::PrivateKey {
    let keyfile = std::fs::File::open(path).expect("cannot open private key file");
    let mut reader = std::io::BufReader::new(keyfile);
    let keys = rustls_pemfile::rsa_private_keys(&mut reader).unwrap();
    rustls::PrivateKey(keys[0].clone())
}

2. Enforce Strong Cipher Suites and TLS Version

Restrict the server to modern, secure protocols and cipher suites to prevent downgrade attacks.

use tokio_rustls::rustls::{ProtocolVersion, CipherSuite};

let mut server_config = ServerConfig::builder()
    .with_safe_defaults()
    .with_client_cert_verifier(Arc::new(MyClientVerifier::new()))
    .with_protocol_versions(&[&rustls::version::TLS13, &rustls::version::TLS12])
    .expect("invalid protocol version")
    .with_cipher_suites(vec![
        CipherSuite::TLS13_CHACHA20_POLY1305_SHA256,
        CipherSuite::TLS13_AES_256_GCM_SHA384,
        CipherSuite::TLS13_AES_128_GCM_SHA256,
    ]);

3. Validate Client Certificate Fields for Authorization

After mTLS handshake, use claims from the client certificate (e.g., subject alternative names) for fine-grained access control, and ensure this logic is covered by authorization tests.

// Example: extract Common Name (CN) from client certificate for role-based checks
fn get_common_name(cert: &Certificate) -> Option<String> {
    use openssl::x509::X509;
    let x509 = X509::from_der(&cert.0).ok()?;
    let subject_name = x509.subject_name();
    let entries = subject_name.entries_by_nid(openssl::nid::Nid::COMMONNAME);
    entries.into_iter().next().and_then(|e| e.data().as_utf8_str().ok().map(String::from))
}

These steps align the implementation with security best practices and reduce the risk of authentication bypass, insecure configuration, and authorization flaws. middleBrick’s scans can validate that mTLS is correctly enforced and that no unauthenticated paths remain.

Frequently Asked Questions

Can mTLS in Axum inadvertently allow unauthenticated access if client certificates are not validated?
Yes. If the server does not enforce client certificate verification (e.g., missing or misconfigured client CA checks), the endpoint may accept requests without valid client certificates, effectively bypassing mTLS and exposing unauthenticated attack surfaces.
How does middleBrick help detect mTLS misconfigurations in Axum-based APIs?
middleBrick scans the unauthenticated attack surface and correlates findings with expected mTLS requirements defined in the API specification. It flags missing client verification, weak cipher suites, and inconsistent authorization logic that could arise from mTLS misconfigurations.