HIGH request smugglingactixmutual tls

Request Smuggling in Actix with Mutual Tls

Request Smuggling in Actix with Mutual Tls — how this specific combination creates or exposes the vulnerability

Request smuggling arises when an HTTP proxy or intermediary parses requests differently than the origin server. In Actix with mutual TLS (mTLS), the frontend termination setup can create a parsing mismatch: the frontend terminates TLS and forwards the request to Actix over a separate TLS connection. If the frontend and Actix parse headers, chunked encoding, or content-length values differently, an attacker can craft a request that is interpreted as two distinct requests by the two layers. A common pattern is smuggling via Content-Length and Transfer-Encoding confusion, where the frontend accepts a request with both headers and Actix processes the second request as a separate, potentially unauthorized, operation.

With mTLS, the frontend validates client certificates before forwarding, but if Actix also enforces client certificate validation independently, the double validation surface can be leveraged. An attacker who can control the client certificate presented to the frontend might induce the frontend to forward a request where the Host or X-Forwarded-For headers are altered in a way that Actix trusts a different origin. Because Actix may not be aware of the frontend’s normalization, the smuggled request can bypass intended routing or authorization checks, reaching an endpoint that should have been restricted. This is especially risky when Actix routes are sensitive (e.g., administrative or data-modifying endpoints) and the frontend applies broader allowlists.

For example, an attacker could send a request like:

POST / HTTP/1.1
Host: frontend.example.com
Content-Length: 53
Transfer-Encoding: chunked

0

GET /admin/export HTTP/1.1
Host: actix-internal.example.com
Content-Length: 0

If the frontend parses Content-Length and ignores Transfer-Encoding (or vice versa), and Actix parses the second request with its own rules, the second request may be processed as a separate, authenticated call to /admin/export. Because mTLS ensures a client certificate is presented, Actix may trust the request as coming from an authenticated client, even though the frontend never intended to expose that endpoint. The combination of mTLS trust and inconsistent parsing creates a path where smuggling bypasses intended access controls.

Mutual Tls-Specific Remediation in Actix — concrete code fixes

Mitigate smuggling by ensuring consistent parsing and strict header handling between the frontend and Actix. Configure the frontend to remove or reject ambiguous headers before forwarding, and make Actix enforce strict header validation. Do not rely on client certificates to enforce authorization; treat mTLS as authentication of the client to the frontend only, and propagate identity via explicit, normalized headers (e.g., x-forwarded-user) that Actix validates independently.

In Actix web, you can enforce header normalization and reject requests that contain ambiguous or duplicate parsing signals. Below are concrete examples that show how to set up mTLS in Actix and add middleware to mitigate smuggling risks.

1. Actix mTLS server configuration (rust)

use actix_web::{web, App, HttpServer, middleware::Logger};
use actix_web::http::header::{self, HeaderValue};
use actix_web::dev::ServiceRequest;
use actix_web::error::ErrorBadRequest;
use actix_web::middleware::Next;
use std::sync::Arc;

async fn validate_no_smuggling_headers(req: ServiceRequest, next: Next) -> Result {
    // Reject requests that contain both Content-Length and Transfer-Encoding
    let has_cl = req.headers().get(header::CONTENT_LENGTH).is_some();
    let has_te = req.headers().get(header::TRANSFER_ENCODING).is_some();
    if has_cl && has_te {
        return Err(ErrorBadRequest("Ambiguous encoding headers"));
    }
    // Normalize and remove hop-by-hop headers before passing downstream
    let mut req = req;
    req.headers_mut().remove(header::PROXY_AUTHENTICATE);
    req.headers_mut().remove(header::PROXY_AUTHORIZATION);
    req.headers_mut().remove(header::TE);
    next.call(req).await
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(move || {
        App::new()
            .wrap(Logger::default())
            .wrap_fn(|req, next| validate_no_smuggling_headers(req, next))
            .service(web::resource("/api/endpoint").to(|| async { "ok" }))
    })
    .bind_rustls_https(
        "0.0.0.0:8443",
        rustls::ServerConfig::builder()
            .with_safe_defaults()
            .with_client_auth_cert(vec![] /* load client CA certs */)
            .map_err(|_| std::io::Error::new(std::io::ErrorKind::Other, "bad certs"))?
    )?
    .await
}

2. Frontend-to-Actix header normalization (conceptual)

The frontend should strip or overwrite headers that could be misinterpreted and inject a single, canonical identifier for the authenticated end-user. Actix should trust this identifier only if it is cryptographically bound to the mTLS certificate presented to the frontend.

// Frontend pseudo-code: normalize before forwarding
req.headers.delete('transfer-encoding');
req.headers.set('x-forwarded-user', endUserFromClientCert);
// Ensure Content-Length is set only if body is present and fixed
if (body) req.headers.set('content-length', String(body.length));

3. Actix strict header validation (rust)

async fn require_trusted_host(req: ServiceRequest, next: Next) -> Result {
    let host = req.headers().get("host");
    let expected = HeaderValue::from_static("api.example.com");
    if host != Some(&expected) {
        return Err(ErrorBadRequest("Unexpected host"));
    }
    // Ensure x-forwarded-user is present and valid (e.g., verified via a mapping to client cert)
    if req.headers().get("x-forwarded-user").is_none() {
        return Err(ErrorBadRequest("Missing forward identity"));
    }
    next.call(req).await
}

Combine these practices: reject ambiguous encoding headers, strip smuggling-relevant hop-by-hop headers, normalize and propagate identity explicitly, and validate Host and injected headers on Actix. This reduces the risk that mTLS-enabled deployments inadvertently expose endpoints via smuggling.

Frequently Asked Questions

Does mTLS prevent request smuggling in Actix?
No. Mutual TLS authenticates the client to the frontend but does not prevent header-parsing mismatches between the frontend and Actix. Smuggling depends on how each layer parses headers and encoding; mTLS alone does not fix those inconsistencies.
What is the most important mitigation specific to mTLS setups?
Ensure the frontend normalizes and removes hop-by-hop and ambiguous headers (e.g., Transfer-Encoding, proxy-specific headers) before forwarding, and have Actix enforce strict header validation and not rely on client certificates for authorization.