HIGH hallucination attacksaxummutual tls

Hallucination Attacks in Axum with Mutual Tls

Hallucination Attacks in Axum with Mutual Tls — how this specific combination creates or exposes the vulnerability

In an Axum service that enforces mutual TLS (mTLS), a hallucination attack can occur when a client presents a valid certificate but is not authorized to access specific resources or to invoke certain endpoints. The server validates the TLS handshake and may assign an identity (e.g., subject or certificate hash) to the request, but authorization logic is missing or insufficient. This mismatch between authentication (mTLS) and authorization creates a path for attackers to "hallucinate" access to data or operations they should not reach. Because mTLS ensures transport-layer identity, developers can mistakenly assume that authentication equals authorization, leading to missing checks at the application layer.

An attacker with a legitimate certificate might probe unauthenticated or weakly protected endpoints, testing for IDOR or BOLA-style behavior. Even though the TLS layer provides identity, Axum routes will process requests according to the handler logic; if handlers do not validate scopes, roles, or tenant context, the request proceeds as if the client is authorized. The risk is compounded when handlers trust metadata derived from the certificate (e.g., CN or SAN) without additional constraints. For example, a handler might use certificate fields to build a user context but fail to verify that the user owns the target resource. This allows the attacker to hallucinate access by iterating through IDs or guessing relationships. Input validation and rate limiting remain essential because mTLS does not sanitize or constrain the content of the request itself.

LLM/AI security considerations appear when services expose endpoints that return generated content or system prompts. If an mTLS-protected endpoint leaks system prompts or exposes model behavior via crafted inputs, attackers can use prompt injection techniques adapted to the API surface. While mTLS secures the channel, it does not prevent adversarial inputs designed to elicit hallucinated responses or to probe for excessive agency in downstream LLM integrations. Data exposure can occur if responses include sensitive information tied to the authenticated certificate’s identity but intended for a different context. Therefore, Axum services must couple mTLS with explicit authorization checks, scoped policies, and input validation to prevent hallucination attacks across transport identity, application logic, and AI security dimensions.

Mutual Tls-Specific Remediation in Axum — concrete code fixes

Remediation requires enforcing authorization after mTLS authentication in Axum. Use extractor patterns to retrieve peer identity from the request extensions populated by the TLS layer, then validate permissions before processing. Below are concrete, working examples that combine mTLS setup with authorization guards.

1. mTLS configuration with rustls and Axum extractors

use axum::Router;
use std::net::SocketAddr;
use tokio_rustls::rustls::{ServerConfig, Certificate, PrivateKey};
use tokio_rustls::TlsAcceptor;
use std::sync::Arc;

async fn build_router() -> Router {
    Router::new()
        .route("/api/resource/:id", axum::routing::get(handle_resource))
}

#[tokio::main]
async fn main() {
    let certs = load_certs("certs/server.crt").expect("failed to load cert");
    let key = load_private_key("certs/server.key").expect("failed to load key");
    let config = ServerConfig::builder()
        .with_safe_defaults()
        .with_no_client_auth() // we will handle auth manually after TLS
        .with_single_cert(certs, key)
        .expect("invalid cert or key");
    let acceptor = TlsAcceptor::from(Arc::new(config));

    let listener = tokio::net::TcpListener::bind("0.0.0.0:8443").await.unwrap();
    loop {
        let (stream, _) = listener.accept().await.unwrap();
        let acceptor = acceptor.clone();
        tokio::spawn(async move {
            let tls_stream = acceptor.accept(stream).await.unwrap();
            // Extract peer cert for identity
            let peer_certs = tls_stream.get_ref().1.get_peer_certificates();
            // You can store peer_certs in request extensions here via a layer
        });
    }
}

fn load_certs(path: &str) -> std::io::Result<Vec<Certificate>> {
    let certfile = std::fs::File::open(path)?;
    let mut reader = std::io::BufReader::new(certfile);
    rustls_pemfile::certs(&mut reader).collect()
}

fn load_private_key(path: &str) -> std::io::Result<PrivateKey> {
    let keyfile = std::fs::File::open(path)?;
    let mut reader = std::io::BufReader::new(keyfile);
    let keys = rustls_pemfile::pkcs8_private_keys(&mut reader)?;
    keys.into_iter().next().ok_or_else(|| std::io::Error::new(std::io::ErrorKind::InvalidData, "no private key"))
}

2. Axum extractor that validates authorization after mTLS

use axum::async_trait;
use axum::extract::{Extension, FromRequest};
use axum::http::request::Parts;
use std::convert::Infallible;
use std::sync::Arc;

struct MtlsIdentity {
    pub subject: String,
    pub scopes: Vec<String>,
}

struct AuthorizedResource {
    pub user_id: String,
}

#[async_trait]
impl FromRequest<S> for AuthorizedResource
where
    S: Send + Sync,
{
    type Rejection = (axum::http::StatusCode, String);

    async fn from_request(req: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
        // Retrieve identity from request extensions (populated by a middleware layer)
        let identity = req.extensions.get::<MtlsIdentity>()
            .ok_or_else(|| (axum::http::StatusCode::FORBIDDEN, "missing identity".to_string()))?;

        // Example: ensure the identity has a required scope
        if !identity.scopes.contains("read:resource".to_string()) {
            return Err((axum::http::StatusCode::FORBIDDEN, "insufficient scope".to_string()));
        }

        // Example: enforce ownership or tenant check (BOLA/IDOR prevention)
        let resource_id = req.uri.path().split("/").last().unwrap_or("");
        // Replace with actual lookup: does this identity own this resource?
        if !is_authorized_resource(&identity.subject, resource_id) {
            return Err((axum::http::StatusCode::FORBIDDEN, "access to resource denied".to_string()));
        }

        Ok(AuthorizedResource { user_id: identity.subject.clone() })
    }
}

fn is_authorized_resource(subject: &str, resource_id: &str) -> bool {
    // Implement your policy: e.g., check a mapping subject -> owned resources
    subject == resource_id // simplistic example
}

async fn handle_resource(req: axum::http::Request<axum::body::Body>) -> axum::http::Response<String> {
    // If AuthorizedResource was extracted successfully, proceed
    axum::http::Response::new("secure resource data".to_string())
}

3. Middleware to populate identity from client cert

use axum::middleware::{self, Next};
use axum::extract::Request;
use axum::http::Response;
use std::sync::Arc;

async fn mtls_identity_middleware(
    request: Request,
    next: Next,
) -> Response {
    // Extract peer certs from request extensions (set by the TLS layer)
    let peer_certs = request.extensions().get::<Vec<Vec<u8>>>()
        .cloned()
        .unwrap_or_default();

    let identity = if let Some(certs) = peer_certs.first() {
        // Parse subject DN; use `openssl` or `webpki` in production
        parse_subject_from_cert(certs)
    } else {
        "unknown".to_string()
    };

    let scopes = vec!["read:resource".to_string()];
    let identity = MtlsIdentity { subject: identity, scopes };
    let request = request.with_extensions(Arc::new(identity));
    next.run(request).await
}

fn parse_subject_from_cert(cert_der: &[u8]) -> String {
    // Placeholder: implement proper parsing with `x509-parser` or `openssl`
    "CN=alice,O=example".to_string()
}

4. Complementary protections

Combine mTLS with input validation, rate limiting, and explicit authorization checks. mTLS does not prevent over-posting, injection, or LLM-specific risks such as prompt injection or data exfiltration through model outputs. Apply schema validation on request bodies, enforce per-endpoint rate limits, and scope data access by identity to mitigate BOLA/IDOR and hallucination-style abuses across both traditional API misuse and AI-integrated endpoints.

Related CWEs: llmSecurity

CWE IDNameSeverity
CWE-754Improper Check for Unusual or Exceptional Conditions MEDIUM

Frequently Asked Questions

Does mutual TLS alone prevent hallucination attacks in Axum APIs?
No. Mutual TLS authenticates the client at the transport layer but does not enforce application-level authorization. Without explicit checks for scopes, ownership, and tenant context, an authenticated client can hallucinate access to resources they should not reach.
How does middleBrick relate to mTLS and hallucination risks in Axum?
middleBrick scans unauthenticated attack surfaces and can surface authorization gaps that make hallucination attacks possible, even when mTLS is used. Its findings map to frameworks like OWASP API Top 10 and include remediation guidance to help you couple mTLS with proper authorization in Axum.