HIGH pii leakageaxummutual tls

Pii Leakage in Axum with Mutual Tls

Pii Leakage in Axum with Mutual Tls — how this specific combination creates or exposes the vulnerability

Transport-layer security is often assumed to fully protect data in transit, but in Axum applications using mutual TLS (mTLS), misconfiguration can still lead to PII leakage at runtime. PII leakage occurs when sensitive data such as email addresses, usernames, or authentication tokens are exposed through logs, error messages, or responses even though the channel is encrypted. With mTLS, the server validates client certificates, which adds identity assurance, but it does not inherently prevent application-layer data exposure.

In Axum, developers may inadvertently include sensitive data in structured logs, debug output, or serialization formats. When mTLS is enabled, traffic is inspected by middleware or filters that terminate TLS and forward requests internally; if these components log request or response bodies without redaction, PII can be captured. Real-world attack patterns like insecure direct object references (IDOR) or improper error handling can compound the issue by exposing user-specific data alongside PII. For example, an endpoint returning a user profile might include email and name fields; if the response is logged in full, PII is exposed even though mTLS protected the transport.

Additionally, certificate-based authentication in mTLS does not limit what the server returns to the client. An Axum handler that does not apply the same data minimization principles as transport security can leak PII through verbose responses or detailed error traces. During middleware inspection, if request payloads containing PII are echoed for debugging, or if panics produce stack traces with user input, the encrypted tunnel does not prevent leakage at the application layer. The combination of mTLS and Axum thus requires deliberate controls: strict logging policies, output sanitization, and careful handling of sensitive fields to ensure PII is not inadvertently persisted or exposed after TLS termination.

Mutual Tls-Specific Remediation in Axum — concrete code fixes

Remediation focuses on ensuring that PII is never logged, serialized, or returned unnecessarily, even when mTLS is in place. In Axum, you can structure handlers and middleware to filter sensitive fields before any logging or serialization occurs. Below are concrete, working examples that demonstrate secure practices with mTLS-aware handling.

1. Minimal logging middleware that redacts PII

Use tower layers to inspect requests and responses without exposing PII. This example shows a custom middleware that scrubs sensitive fields from logs.

use axum::{http::Request, middleware::Next, response::Response, Json};
use std::future::ready;
use tower::Layer;
use tracing::info;

#[derive(Clone)]
pub struct SensitiveDataFilter;

impl Layer for SensitiveDataFilter {
    type Service = SensitiveService;

    fn layer(&self, inner: S) -> Self::Service {
        SensitiveService { inner }
    }
}

pub struct SensitiveService {
    inner: S,
}

impl tower::Service> for SensitiveService
where
    S: tower::Service, Response = Response> + Clone + Send + 'static,
    S::Future: Send + 'static,
    B: Send + 'static,
{
    type Response = Response;
    type Error = S::Error;
    type Future = async::BoxFuture<'static, Result>;

    fn poll_ready(&mut self, cx: &mut std::task::Context<'_>) -> std::task::Poll> {
        self.inner.poll_ready(cx)
    }

    fn call(&mut self, mut req: Request) -> Self::Future {
        // Redact PII from request logs
        let redacted_uri = redact_pii_from_uri(req.uri().to_string());
        info!(method = %req.method(), uri = redacted_uri, "request received");

        let inner = self.inner.clone();
        async move {
            let res = inner.call(req).await?;
            // Response logging should also avoid PII; here we assume status-only logging
            info!(status = %res.status(), "response sent");
            Ok(res)
        }.into()
    }
}

fn redact_pii_from_uri(uri: String) -> String {
    // Simple redaction for query parameters like email or name
    let url = url::Url::parse(&uri).ok().unwrap_or_else(|| url::Url::parse("http://example.com").unwrap());
    let mut pairs: Vec<(String, String)> = url.query_pairs().filter(|(k, _)| {
        !matches!(k.as_ref(), "email" | "name" | "ssn")
    }).map(|(k, v)| (k.to_string(), v.to_string())).collect();
    let mut redacted = url::Url::parse("http://example.com").unwrap();
    redicted.set_query(Some(&serde_urlencoded::to_string(&pairs).unwrap_or_default()));
    redacted.to_string()
}

2. Handler design to exclude PII from responses

Define response DTOs that omit sensitive fields unless explicitly required. This ensures that even if mTLS authenticates a client, the server does not return unnecessary PII.

use axum::{routing::get, Router};
use serde::Serialize;

#[derive(Serialize)]
pub struct PublicUser {
    pub id: u64,
    pub username: String,
    // email and other PII fields are omitted
}

#[derive(Serialize)]
pub struct FullUser {
    pub id: u64,
    pub username: String,
    pub email: String,
    pub ssn: String,
}

// Use PublicUser for general endpoints
async fn get_user_public() -> PublicUser {
    // Fetch from data layer and map to PublicUser
    PublicUser {
        id: 1,
        username: "alice".to_string(),
    }
}

// Internal or admin endpoints may use FullUser with strict access controls
async fn get_user_full() -> FullUser {
    FullUser {
        id: 1,
        username: "alice".to_string(),
        email: "[email protected]".to_string(),
        ssn: "123-45-6789".to_string(),
    }
}

pub fn app_router() -> Router {
    Router::new()
        .route("/users/me", get(get_user_public))
        // Admin route protected by additional authorization checks
        .route("/users/admin/:id", get(get_user_full))
}

3. Enforcing mTLS and validating client certificates in Axum

Ensure the server requires and validates client certificates, rejecting unauthenticated mTLS handshakes. This prevents unauthorized access that could lead to PII exposure through missing identity checks.

use axum::Server;
use hyper::{server::conn::Http, service::service_fn};
use std::net::SocketAddr;
use tokio_rustls::rustls::{ServerConfig, Certificate, PrivateKey};
use tokio_rustls::TlsAcceptor;

async fn run_mtls_server() {
    let certs = load_certs("path/to/ca.pem");
    let key = load_private_key("path/to/server.key");
    let mut config = ServerConfig::builder()
        .with_safe_defaults()
        .with_client_cert_verifier(Arc::new(MyClientVerifier))
        .with_single_cert(certs, key)
        .expect("invalid cert/key");

    config.client_auth = rustls::server::ClientAuthMode::Required;
    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();
            // Pass TLS stream into Axum service
            let service = service_fn(|req| async { Ok::<_, std::convert::Infallible>(Response::new(Body::empty())) });
            Http::new().serve_connection(tls_stream, service).await.unwrap();
        });
    }
}

fn load_certs(path: &str) -> Vec {
    // Load CA certs for client verification
    vec![]
}

fn load_private_key(path: &str) -> PrivateKey {
    PrivateKey(std::fs::read(path).unwrap())
}

struct MyClientVerifier;
// Implement client cert verification logic here

Related CWEs: dataExposure

CWE IDNameSeverity
CWE-200Exposure of Sensitive Information HIGH
CWE-209Error Information Disclosure MEDIUM
CWE-213Exposure of Sensitive Information Due to Incompatible Policies HIGH
CWE-215Insertion of Sensitive Information Into Debugging Code MEDIUM
CWE-312Cleartext Storage of Sensitive Information HIGH
CWE-359Exposure of Private Personal Information (PII) HIGH
CWE-522Insufficiently Protected Credentials CRITICAL
CWE-532Insertion of Sensitive Information into Log File MEDIUM
CWE-538Insertion of Sensitive Information into Externally-Accessible File HIGH
CWE-540Inclusion of Sensitive Information in Source Code HIGH

Frequently Asked Questions

Does mTLS in Axum automatically prevent PII leakage?
No. Mutual TLS secures the transport and verifies client identity, but it does not prevent application-layer PII leakage. Sensitive data can still be exposed through logs, error messages, or overly verbose responses if handlers and middleware do not explicitly filter or redact PII.
How can I ensure Axum handlers do not leak PII when mTLS is enabled?
Use response DTOs that exclude PII, apply redaction middleware to scrub sensitive fields before logging, and enforce strict output validation. Even with mTLS, design handlers to return only necessary data and avoid echoing request bodies in logs or error traces.