HIGH crlf injectionaxummutual tls

Crlf Injection in Axum with Mutual Tls

Crlf Injection in Axum with Mutual Tls

Crlf Injection occurs when user-controlled data is reflected in HTTP headers without sanitization, allowing an attacker to inject newline characters (\r\n) to split headers and inject malicious ones. In Axum, this typically arises when header values are derived from request inputs such as query parameters or headers. The presence of Mutual Tls (mTLS) changes the context of the risk but does not eliminate it. With mTLS, the client presents a certificate during the TLS handshake, which Axum can use to authenticate the client before processing the request. However, mTLS operates at the transport layer and does not sanitize or validate application-level header values. An authenticated client can still supply a crafted header or query parameter containing \r\n sequences. If Axum constructs response headers using unsanitized input—for example, using a header value from the request directly—the injected newline can lead to response splitting. This can enable cache poisoning, cross-site scripting via injected headers, or smuggling attacks. Because mTLS ensures the identity of the client, the server may trust the request more implicitly, potentially reducing logging or validation for authenticated mTLS sessions, which can amplify the impact of a Crlf Injection flaw. The vulnerability is not in the TLS layer but in how the application handles and reflects user data into headers after the mTLS authentication is complete.

Mutual Tls-Specific Remediation in Axum

Remediation focuses on strict input validation and safe header construction in Axum regardless of mTLS. Never directly use incoming header values, query parameters, or path segments as response header values. Instead, validate and sanitize all inputs, and use a denylist or allowlist approach for known-safe patterns. Below are concrete Axum code examples demonstrating secure handling with mTLS.

Example 1: Safe header setting with input validation

use axum::{routing::get, Router, http::HeaderMap, response::IntoResponse};
use std::net::SocketAddr;

fn is_safe_header_value(value: &str) -> bool {
    // Allow only alphanumeric and a few safe symbols, reject \r and \n
    !value.contains('\r') && !value.contains('\n') && value.chars().all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '_' || c == '.' || c == ' ')
}

async fn handler(headers: HeaderMap) -> impl IntoResponse {
    // Example: take a custom header from the request, validate it, and use a safe default if invalid
    let user_value = headers.get("X-Custom-Input")
        .and_then(|v| v.to_str().ok())
        .filter(|&v| is_safe_header_value(v))
        .unwrap_or("default_value");

    (axum::http::StatusCode::OK, [( "X-Safe-Header", user_value )])
}

#[tokio::main]
async fn main() {
    let app = Router::new().route("/", get(handler));
    let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
    axum::Server::bind(&addr)
        .serve(app.into_make_service())
        .await
        .unwrap();
}

Example 2: Axum middleware with mTLS metadata

use axum::{routing::get, Router, async_trait, middleware, Extension, http::Request, http::Response, body::Body};
use std::future::Future;
use std::pin::Pin;

struct MtlsMetadata {
    client_cert_fingerprint: String,
}

async fn header_sanitizer(req: Request<B>, next: Extension<MtlsMetadata>) -> Pin<Box<dyn Future<Output = Response<Body>> + Send + '_>> {
    // Inspect request headers and sanitize before passing to handler
    let sanitized_headers = req.headers().iter().map(|(name, value)| {
        let val_str = value.to_str().unwrap_or("");
        let safe_val = if val_str.contains('\r') || val_str.contains('\n') {
            "invalid"
        } else {
            val_str
        };
        (name.clone(), safe_val.to_string())
    });

    // Build a new request with sanitized headers if needed, or proceed with original request
    let (parts, body) = req.into_parts();
    let new_req = Request::from_parts(parts, body);
    let response = next.oneshot(new_req).await;
    Box::pin(async move { response.unwrap_or_else(|_| Response::new(Body::from("Internal error"))) })
}

#[tokio::main]
async fn main() {
    let mtls_meta = MtlsMetadata { client_cert_fingerprint: "aa:bb:cc:dd:ee:ff".to_string() };
    let app = Router::new()
        .route("/", get(|| async { "ok" }))
        .layer(middleware::from_fn_with_state((), |_, req, next| header_sanitizer(req, next)));
    
    // In practice, you would integrate mTLS via tls_rust or axum_extra::extract::connect_info
    // This example shows where sanitization would occur in the request lifecycle.
}

Key practices

  • Validate all header inputs against an allowlist of safe characters.
  • Reject or encode \r and \n characters in any user-controlled data used in headers.
  • Do not rely on mTLS to enforce header safety; treat authenticated requests as potentially malicious.
  • Use framework utilities or manual checks to ensure no newline characters are present before setting headers.

Frequently Asked Questions

Does mTLS prevent Crlf Injection in Axum?
No. Mutual Tls authenticates the client at the transport layer but does not sanitize application-level header values. Crlf Injection must be addressed through input validation and safe header construction in Axum code.
What are the signs of Crlf Injection in Axum logs?
Unexpected newline or carriage return characters in header values, malformed log entries showing split lines, or injected headers such as Set-Cookie or Location appearing where they should not. Monitor header inputs that reflect user data.