Token Leakage in Actix with Mutual Tls
Token Leakage in Actix with Mutual TLS — how this specific combination creates or exposes the vulnerability
Token leakage in an Actix service that uses Mutual TLS (mTLS) occurs when authentication material intended to remain confined to the mTLS boundary is exposed beyond it. In an mTLS setup, the server presents a server certificate and requests a client certificate; the TLS channel is established only when the client provides a valid client cert. Once the TLS handshake succeeds, Actix typically terminates TLS and processes HTTP requests as normal. If application code mistakenly treats the mTLS-authenticated identity as authorization or conflates transport-layer client identity with authorization tokens, tokens can be leaked through logs, error messages, or insecure internal APIs.
For example, consider an Actix handler that extracts a bearer token from an Authorization header and then logs the authenticated principal derived from the mTLS certificate without redaction. If the log line includes the token or a session identifier that should remain opaque, an attacker who gains access to logs (or can cause the application to reflect logs in error responses) can see the token. Similarly, if the service forwards requests internally—such as to a downstream API or background worker—and includes the Authorization header without stripping it, the token can be exposed to components that should operate with different trust boundaries. This is a boundary violation: the mTLS channel provides peer authentication and encryption, but the application must not propagate tokens that should be scoped only to the client certificate context.
Another leakage pattern involves error handling. Actix middleware or custom error handlers that include request headers or payloads in verbose error pages can inadvertently surface tokens. For instance, if an integration test or monitoring wrapper inspects responses and echoes headers into diagnostic output, tokens can be copied into unintended channels. Even with mTLS ensuring the client is known, the token itself must not be echoed or cached insecurely. Attack patterns like log injection or insecure debug endpoints can then be chained to exfiltrate the token, bypassing intended access controls that rely on token secrecy rather than mTLS identity alone.
Real-world references: While this is not tied to a single CVE, patterns resembling this have been observed in API services where missing authorization checks (BOLA/IDOR) coincide with verbose logging. OWASP API Top 10 A01:2023 Broken Object Level Authorization and A05:2023 Broken Function Level Authorization are relevant when mTLS identity is incorrectly mapped to token-based authorization. PCI-DSS and SOC2 controls emphasize protecting authentication material in transit and at rest; leaking tokens undermines those controls even when mTLS is correctly configured.
middleBrick can detect scenarios where token handling and mTLS boundaries are misaligned by correlating unauthenticated scans with runtime behavior. Its LLM/AI Security checks look for system prompt leakage and output scanning that could expose tokens in responses, while the Inventory Management and Unsafe Consumption checks highlight missing authorization on endpoints that should validate token scope independently of mTLS.
Mutual TLS-Specific Remediation in Actix — concrete code fixes
To remediate token leakage in Actix with mTLS, ensure that tokens are never derived from or echoed alongside mTLS identities, and that authorization is validated independently. Below are concrete code examples for a secure Actix setup with mTLS that avoids leaking tokens.
1. Configure mTLS in Actix-web
Use native-TLS or rustls to require client certificates. Here is a minimal, realistic server configuration using native-tls with actix-web:
use actix_web::{web, App, HttpServer, Responder};
use actix_web::http::header::HeaderValue;
use std::sync::Arc;
// A minimal handler that does NOT echo or forward the Authorization header.
async fn secure_endpoint() -> impl Responder {
// Perform application-level authorization here, using claims extracted from the mTLS cert.
// Do not log or forward bearer tokens.
"OK"
}
#[actix_web::main]
async fn main() -> std::io::Result<()>) {
let builder = native_tls::TlsAcceptor::builder(
native_tls::Identity::from_pkcs12(
include_bytes!("path/to/server.p12"),
"server_password"
).expect("invalid server identity")
);
let builder = builder.unwrap();
// Require client authentication
builder.builder().set_verify(native_tls::VerifyMode::Peer);
let acceptor = builder.build().expect("TLS acceptor");
HttpServer::new(move || {
App::new()
.wrap(actix_web::middleware::Logger::default())
// Ensure authorization checks are present; do not propagate tokens.
.route("/secure", web::get().to(secure_endpoint))
})
.bind_rustls("0.0.0.0:8443", acceptor)?
.run()
.await
}
2. Avoid logging or forwarding tokens
Ensure handlers and middleware do not include sensitive headers in logs or downstream requests. For example, a middleware that scrubs sensitive headers:
use actix_web::{dev::{ServiceRequest, ServiceResponse}, Error};
use actix_web::body::BoxBody;
use actix_web::http::header;
use actix_web_httpauth::extractors::bearer::BearerAuth;
pub async fn sanitize_and_authorize(
req: ServiceRequest,
payload: &mut actix_web::dev::Payload,
) -> Result {
// Do not extract or forward bearer tokens; validate scope via application claims.
let headers = req.headers();
if let Some(auth) = headers.get(header::AUTHORIZATION) {
// Instead of forwarding, validate and then remove the header if it must be forwarded.
let _bearer = auth.to_str().unwrap_or("");
// Perform token validation and mapping to permissions here.
// After validation, remove the header to prevent leakage.
// Note: In practice, prefer claims from mTLS and avoid bearer tokens entirely.
}
Ok(req)
}
3. Use claims from client certificate, not tokens, for authorization
Map the mTLS identity to application roles without introducing opaque tokens. If tokens must be used, keep them independent from the mTLS context:
use actix_web::{web, HttpResponse};
use openssl::x509::X509;
// Assume extract_peer_cert provides the client certificate after successful mTLS.
fn validate_mtls_peer(peer_cert: Option) -> Result<(), &'static str> {
match peer_cert {
Some(cert) => {
// Map certificate fields to roles/permissions.
let subject = cert.subject_name();
// Perform mapping; do not derive a bearer token from certificate.
if /* has required role */ true {
Ok(())
} else {
Err("insufficient permissions")
}
}
None => Err("client certificate required"),
}
}
Key practices: keep token handling separate from mTLS identity, never log Authorization headers or tokens, validate permissions at the handler level, and scrub sensitive headers in middleware. middleBrick’s GitHub Action can be added to CI/CD pipelines to fail builds if risk scores exceed your threshold, ensuring these patterns are caught before deployment.