HIGH header injectionactixmutual tls

Header Injection in Actix with Mutual Tls

Header Injection in Actix with Mutual Tls — how this specific combination creates or exposes the vulnerability

Header Injection in an Actix-web service that also enforces Mutual TLS (mTLS) can occur when user-controlled input is reflected into HTTP headers without validation. Even with client certificates required at the TLS layer, an authenticated session can still be abused if the application constructs response headers from unchecked sources, such as query parameters, JSON fields, or path segments. For example, concatenating a user-supplied string into a custom header like X-Request-Source may enable header splitting (CRLF characters) that injects additional headers or overrides existing ones.

In an mTLS setup, the server validates the client certificate and may map certificate fields (e.g., subject or SAN) into application logic. If the application then uses those fields to build outgoing headers or to decide routing without strict allowlists, an attacker who possesses a valid client certificate can still influence header values. This creates a scenario where the presence of Mutual Tls may give a false sense of security: transport-layer identity does not prevent application-layer header manipulation. Common outcomes include response splitting, cache poisoning, or unauthorized header injection, which can bypass intended security boundaries despite mTLS being in place.

Actix-web provides request metadata such as peer certificate information via extractors (e.g., CertificateRequest). If developers forward certificate-derived values directly into headers or use them to construct dynamic redirects, they risk introducing injection vectors. For instance, using a certificate’s organizational unit in a Location header without sanitization can enable open redirects or header smuggling when combined with CRLF characters. Therefore, the combination of mTLS and unchecked header usage means that valid authenticated requests can still lead to unsafe header manipulation if input validation and canonicalization are omitted.

Mutual Tls-Specific Remediation in Actix — concrete code fixes

To mitigate Header Injection in Actix with Mutual Tls, treat all header values as untrusted even when derived from certificate fields. Validate and sanitize any data used in header construction, and prefer static or allowlisted values where possible. Below are concrete code examples demonstrating secure handling in Actix-web.

Example 1: Safe header assignment using allowlist values

Map certificate attributes to a predefined set of roles instead of echoing raw certificate fields in headers.

use actix_web::{web, App, HttpResponse, HttpServer, Responder};
use actix_web::http::header::{HeaderName, HeaderValue};
use openssl::x509::X509;

fn role_from_cert(cert: &X509) -> Option<&'static str> {
    // Extract a custom OID or inspect subject fields, then map to an allowlist
    // This is simplified; real code should parse extensions carefully.
    // Return a known role or None.
    Some("service-account")
}

async fn handler(cert: web::ReqData) -> impl Responder {
    if let Some(role) = role_from_cert(&cert) {
        let mut resp = HttpResponse::Ok();
        // Use try_insert to avoid overwriting existing headers unexpectedly
        resp.headers_mut().try_insert(
            HeaderName::from_static("x-user-role"),
            HeaderValue::from_static(role),
        ).ok();
        resp.body("OK")
    } else {
        HttpResponse::Forbidden().finish()
    }
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new()
            .app_data(web::ReqData::default())
            .route("/secure", web::get()._to(handler))
    })
    .bind_openssl("127.0.0.1:8443", /* ssl config */ unimplemented!())
    .unwrap()
    .run()
    .await
}

Example 2: Rejecting header injection characters and canonicalizing input

When constructing headers from any user-influenced source (including certificate fields), explicitly reject CRLF sequences and normalize whitespace.

use actix_web::{web, App, HttpResponse, HttpServer, Responder, http::header::HeaderValue};

fn is_safe_header_value(value: &str) -> bool {
    // Reject CR and LF to prevent header splitting or injection
    !value.contains('\r') && !value.contains('\n')
}

async fn custom_header(name: String, value: String) -> impl Responder {
    if !is_safe_header_value(&name) || !is_safe_header_value(&value) {
        return HttpResponse::BadRequest().body("Invalid header value");
    }
    let mut resp = HttpResponse::Ok();
    // Use try_insert to avoid accidental overrides; ensure HeaderName is validated separately
    if let Ok(hn) = name.parse::() {
        if let Ok(hv) = value.parse::() {
            resp.headers_mut().insert(hn, hv);
        }
    }
    resp.body("Header set safely")
}

// In routes, bind custom_header to a POST endpoint that receives JSON or form data.

General practices for Actix with mTLS

  • Do not directly concatenate certificate fields into headers; map them to controlled identifiers.
  • Validate all header names and values for disallowed characters (e.g., \r, \n) before insertion.
  • Use try_insert to avoid unintentionally replacing security-critical headers.
  • Apply the same sanitization to incoming and outgoing headers when acting as a proxy or gateway, even when mTLS is enforced.

Frequently Asked Questions

Does mTLS eliminate the risk of header injection in Actix?
No. Mutual TLS provides transport-layer client authentication but does not prevent application-layer header manipulation. If your Actix service uses certificate-derived data in headers without validation, header injection can still occur.
What should I validate when building headers from certificate fields in Actix?
Treat certificate fields as untrusted input. Validate and map them to a strict allowlist, reject any values containing carriage return or line feed characters, and avoid using raw certificate strings directly in header names or values.