HIGH header injectionaxumbasic auth

Header Injection in Axum with Basic Auth

Header Injection in Axum with Basic Auth — how this specific combination creates or exposes the vulnerability

Header Injection occurs when user-controlled input is reflected into HTTP headers without validation or sanitization. In Axum, combining Basic Authentication with unchecked header values can lead to response splitting, header manipulation, or authentication bypass. Basic Auth in Axum is typically handled by inspecting the Authorization header, decoding the base64-encoded credentials, and validating them against a user store. If the application uses user-controlled data to construct other headers or redirects, an attacker can inject newline characters (e.g., %0a or %0d) to append additional headers such as Set-Cookie or Location, potentially enabling session fixation or open redirects.

Consider an Axum handler that logs the username from Basic Auth into a custom response header for debugging:

use axum::{
    body::Body,
    extract::Request,
    response::{Response, IntoResponse},
    routing::get,
    Router,
};
use std::net::SocketAddr;

async fn auth_handler(
    request: Request,
) -> Result {
    let auth_header = request.headers().get("authorization")
        .ok_or((axum::http::StatusCode::UNAUTHORIZED, "Missing auth".to_string()))?;
    let auth_str = auth_header.to_str().map_err(|_| (axum::http::StatusCode::UNAUTHORIZED, "Invalid header".to_string()))?;
    if !auth_str.starts_with("Basic ") {
        return Err((axum::http::StatusCode::UNAUTHORIZED, "Unsupported scheme".to_string()));
    }
    let decoded = base64::decode(&auth_str[6..]).map_err(|_| (axum::http::StatusCode::UNAUTHORIZED, "Invalid base64".to_string()))?;
    let credentials = String::from_utf8(decoded).map_err(|_| (axum::http::StatusCode::UNAUTHORIZED, "Invalid utf8".to_string()))?;
    let parts: Vec<&str> = credentials.split(':').collect();
    if parts.len() != 2 {
        return Err((axum::http::StatusCode::UNAUTHORIZED, "Invalid credentials format".to_string()));
    }
    let username = parts[0];
    // Debug header vulnerable to injection if username contains newlines
    let mut response = Response::new(Body::from("Authenticated"));
    response.headers_mut().insert("X-User", username.parse().unwrap());
    Ok(response)
}

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

If the username contains sequences like \r\nSet-Cookie: session=attacker, the header injection can manipulate the response. This creates a path for HTTP response splitting, which may allow attackers to inject arbitrary headers, set malicious cookies, or redirect the client. Since Basic Auth credentials are only base64-encoded (not encrypted), the username and password are easily decoded, exposing them in logs or via injection. The risk is higher in APIs that reflect user input into headers without strict allow-listing or sanitization.

In the context of middleBrick’s security checks, this pattern would be flagged under BFLA/Privilege Escalation and Unsafe Consumption checks due to improper handling of authenticated input, and under Data Exposure if credentials are leaked via injected headers.

Basic Auth-Specific Remediation in Axum — concrete code fixes

To mitigate Header Injection in Axum when using Basic Auth, avoid reflecting untrusted input into headers. If you must include user data in responses, use strict allow-listing and encoding. Prefer structured logging over custom headers for debugging credentials. Below are concrete, secure patterns.

1. Validate and sanitize before use

Ensure the username and password do not contain newline characters before using them anywhere in headers or logs. Reject suspicious input early.

fn is_valid_username(username: &str) -> bool {
    !username.contains(|c| c == '\r' || c == '\n')
}

fn is_valid_password(password: &str) -> bool {
    !password.contains(|c| c == '\r' || c == '\n')
}

2. Do not reflect credentials in response headers

Instead of echoing the username in a header, use a sanitized user identifier or a session token. If you must pass data, use a short-lived token mapped server-side.

use axum::{routing::get, Router};
use std::net::SocketAddr;

async fn safe_auth_handler(
    request: axum::http::Request,
) -> Result {
    let auth_header = request.headers().get("authorization")
        .ok_or((axum::http::StatusCode::UNAUTHORIZED, "Missing auth".to_string()))?;
    let auth_str = auth_header.to_str().map_err(|_| (axum::http::StatusCode::UNAUTHORIZED, "Invalid header".to_string()))?;
    if !auth_str.starts_with("Basic ") {
        return Err((axum::http::StatusCode::UNAUTHORIZED, "Unsupported scheme".to_string()));
    }
    let decoded = base64::decode(&auth_str[6..]).map_err(|_| (axum::http::StatusCode::UNAUTHORIZED, "Invalid base64".to_string()))?;
    let credentials = String::from_utf8(decoded).map_err(|_| (axum::http::StatusCode::UNAUTHORIZED, "Invalid utf8".to_string()))?;
    let parts: Vec<&str> = credentials.split(':').collect();
    if parts.len() != 2 {
        return Err((axum::http::StatusCode::UNAUTHORIZED, "Invalid credentials format".to_string()));
    }
    let username = parts[0];
    let password = parts[1];

    if !is_valid_username(username) || !is_valid_password(password) {
        return Err((axum::http::StatusCode::UNAUTHORIZED, "Invalid characters in credentials".to_string()));
    }

    // Perform authentication check here (e.g., verify password hash)
    // On success, do NOT reflect username in headers; use a session or token.
    Ok(axum::response::Response::new(axum::body::Body::from("Authenticated")))
}

3. Use middleware to reject dangerous headers

Add a layer that scans incoming headers for newline characters and rejects malformed requests before they reach your handlers.

use axum::{body::Body, body::boxed, BoxBody, Request, Response};
use std::task::{Context, Poll};
use tower::Service;

struct HeaderValidationLayer;

impl tower::Layer for HeaderValidationLayer {
    type Service = HeaderValidationService;

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

struct HeaderValidationService {
    inner: S,
}

impl Service for HeaderValidationService
where
    S: Service, Error = axum::BoxError>,
{
    type Response = S::Response;
    type Error = S::Error;
    type Future = S::Future;

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

    fn call(&mut self, req: ReqBody) -> Self::Future {
        if let Some(headers) = req.headers() {
            for (name, value) in headers.iter() {
                if let Ok(v) = value.to_str() {
                    if v.contains('\r') || v.contains('\n') {
                        return futures::future::err(axum::BoxError::msg("Invalid header value"));
                    }
                }
            }
        }
        self.inner.call(req)
    }
}

These steps reduce the attack surface by preventing newline injection and avoiding reflection of raw credentials, aligning with secure handling of Basic Auth in Axum.

For ongoing monitoring, consider tools like middleBrick’s CLI (middlebrick scan <url>) to detect header manipulation patterns and other misconfigurations in unauthenticated scans. The GitHub Action can enforce security gates in CI/CD, and the MCP Server allows you to scan APIs directly from your IDE during development.

Frequently Asked Questions

Can Basic Auth headers be safely logged in Axum?
Avoid logging raw Basic Auth headers. If necessary, redact or hash the credentials before logging, and never log the password component. Prefer structured authentication events without echoing raw header values.
Does middleBrick detect header injection in Basic Auth flows?
Yes. middleBrick’s Unsafe Consumption and BFLA checks include header reflection tests. It scans unauthenticated endpoints and flags patterns where user input may be reflected into HTTP headers, including via Basic Auth handling.