HIGH broken authenticationaxummutual tls

Broken Authentication in Axum with Mutual Tls

Broken 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 and validate digital certificates. In an Axum application, mTLS is typically enforced at the TLS listener level before requests reach your Rust handlers. When mTLS is misconfigured or when application-layer authentication is added on top without care, it can create a broken authentication scenario where the presence of a client certificate is assumed to be sufficient for authorization.

One common pattern is to inspect the certificate presented by the client (e.g., via peer certificate APIs) and then skip additional authentication checks in Axum handlers. If the handler does not independently verify identity and permissions—such as mapping the certificate to a user or role and validating scope—an attacker who obtains a valid certificate for any identity can access endpoints they should not reach. This is a Broken Authentication risk: the system trusts the mTLS layer completely and omits application-level authorization checks.

Another specific risk arises when mTLS is used for one endpoint but not consistently enforced across the service. An Axum router might apply TLS with client cert verification to a subset of routes, while other routes remain open or fall back to weaker authentication. This inconsistency exposes higher-privilege operations to unauthenticated or weakly authenticated access. Additionally, if certificate validation logic is incomplete (e.g., not checking revocation via CRL or OCSP), an attacker could use a revoked or compromised certificate to authenticate successfully.

Consider an endpoint designed to retrieve user profiles. If the Axum handler only checks that a client certificate was presented, but does not ensure the certificate belongs to the profile being requested, a horizontally privileged access flaw occurs. The mTLS layer confirms a client is who they claim via cryptography, but the application fails to enforce that the authenticated principal matches the resource. This maps to common weaknesses such as missing authorization checks after authentication and reflects issues seen in IDOR-type vulnerabilities.

Operational practices can also contribute. Rotating certificates and managing revocation is harder when application-layer identity is not independently validated. A compromised certificate or an over-permissive certificate issuance policy can lead to persistent access unless the application validates each request’s permissions. Therefore, mTLS in Axum should be treated as authentication, not as a replacement for explicit authorization and session management within the application.

Mutual Tls-Specific Remediation in Axum — concrete code fixes

To fix Broken Authentication when using mTLS in Axum, enforce explicit identity extraction and authorization checks in handlers, and ensure consistent policy application across all routes. Below are concrete, syntactically correct code examples using the rustls ecosystem with Axum.

Setting up mTLS in Axum with rustls

Configure your Axum server to require client certificates and validate them against a trusted CA. This example builds an HTTPS service that extracts and inspects the peer certificate on each request.

use axum::Router;
use std::net::SocketAddr;
use tokio_rustls::rustls::{Certificate, ServerConfig, NoClientAuth, RootCertStore};
use tokio_rustls::rustls::pki_types::{CertificateDer, PrivateKeyDer};
use tokio_rustls::TlsAcceptor;
use std::sync::Arc;

async fn make_tls_config(ca_path: &str) -> std::io::Result> {
    let mut root_store = RootCertStore::empty();
    let ca_cert = tokio::fs::read(ca_path).await?;
    // Parse PEM or DER CA; this is a simplification for example purposes
    for cert in rustls_pemfile::certs(&mut &ca_cert[..]) {
        root_store.add(CertificateDer::from(cert?));
    }
    let mut server_config = ServerConfig::builder()
        .with_no_client_auth()
        .with_single_cert(vec![], PrivateKeyDer::Pkcs8(vec![]))?; // placeholder: provide server cert+key
    // Require and validate client certificates
    server_config.client_auth_root_store = Arc::new(root_store);
    server_config.client_auth_mode = Some(tokio_rustls::rustls::server::ClientAuthMode::Required);
    Ok(Arc::new(server_config))
}

#[tokio::main]
async fn main() -> std::io::Result<()> {
    let tls_config = make_tls_config("path/to/ca.pem").await?;
    let tls_acceptor = TlsAcceptor::from(tls_config);
    let addr = SocketAddr::from(([127, 0, 0, 1], 3000));

    let app = Router::new()
        .route("/profile", axum::routing::get(get_profile));

    let listener = tokio::net::TcpListener::bind(&addr).await?;
    loop {
        let (stream, _addr) = listener.accept().await?;
        let tls = tls_acceptor.accept(stream).await?;
        let service = app.clone().into_make_service();
        tokio::task::spawn(async move {
            service.await.unwrap();
        });
    }
}

Extracting client identity and enforcing authorization

In your handler, extract the peer certificate and map it to an application identity. Then apply authorization to ensure the authenticated principal is allowed to access the requested resource.

use axum::extract::Request;
use axum::middleware::Next;
use axum::response::Response;
use std::collections::HashMap;
use std::sync::Mutex;

// Simple in-memory mapping for example; use a proper store in production
struct AuthState {
    // cert fingerprint or subject -> roles
    identities: Mutex>>,
}

async fn get_profile(
    axum::extract::Path(name): axum::extract::Path,
    state: axum::extract::State<Arc<AuthState>>,
    // We expect the middleware to have attached an identity derived from the certificate
    axum::extract::Extension(identity): axum::extract::Extension<String>,
) -> Result<String, (axum::http::StatusCode, String)> {
    // Authorization: ensure the authenticated identity matches the requested profile
    if identity != name {
        return Err((axum::http::StatusCode::FORBIDDEN, "Unauthorized access".to_string()));
    }
    Ok(format!("Profile for {}", name))
}

// Middleware to extract and attach identity from client certificate
async fn cert_identity_middleware(
    request: Request,
    next: Next,
    state: axum::extract::State<Arc<AuthState>>,
) -> Response {
    // Extract peer certificate from the request extensions (populated by TLS layer)
    let cert_fingerprint = match extract_cert_fingerprint(&request) {
        Some(fp) => fp,
        None => return Response::builder()
            .status(axum::http::StatusCode::UNAUTHORIZED)
            .body("Missing client certificate".into())
            .unwrap(),
    };

    let state = state.identities.lock().unwrap();
    if !state.identities.contains_key(&cert_fingerprint) {
        return Response::builder()
            .status(axum::http::StatusCode::FORBIDDEN)
            .body("Certificate not trusted".into())
            .unwrap();
    }
    // Attach identity to request extensions for downstream handlers
    let extension = Extension(cert_fingerprint);
    next.run(request.with_extension(extension)).await
}

fn extract_cert_fingerprint(request: &Request) -> Option<String> {
    // In practice, use the TLS stream's peer certificate; this is simplified
    request.extensions().get().cloned()
}

Key remediation points:

  • Require and validate client certificates on the server (ClientAuthMode::Required).
  • Map certificates to application identities in a verifiable store and perform explicit authorization in each handler or via middleware.
  • Ensure mTLS is applied consistently to all routes that require authentication.
  • Check revocation when possible and rotate certificates regularly.

Related CWEs: authentication

CWE IDNameSeverity
CWE-287Improper Authentication CRITICAL
CWE-306Missing Authentication for Critical Function CRITICAL
CWE-307Brute Force HIGH
CWE-308Single-Factor Authentication MEDIUM
CWE-309Use of Password System for Primary Authentication MEDIUM
CWE-347Improper Verification of Cryptographic Signature HIGH
CWE-384Session Fixation HIGH
CWE-521Weak Password Requirements MEDIUM
CWE-613Insufficient Session Expiration MEDIUM
CWE-640Weak Password Recovery HIGH

Frequently Asked Questions

Can mTLS alone replace application-level authentication in Axum?
No. mTLS provides transport-layer identity, but you should still extract certificate details and enforce application-level authorization to prevent Broken Authentication and horizontal privilege escalation.
What should I do if my Axum routes are inconsistent in requiring mTLS?
Apply mTLS uniformly across all routes that access sensitive resources, and use middleware to reject requests lacking valid client certificates to avoid inconsistent enforcement.