HIGH request smugglingaxumbasic auth

Request Smuggling in Axum with Basic Auth

Request Smuggling in Axum with Basic Auth — how this specific combination creates or exposes the vulnerability

Request smuggling occurs when an application processes the same request differently between a frontend proxy/load balancer and the origin application. In Axum, combining custom Basic Auth parsing with non-strict HTTP handling can amplify the risk because mismatched expectations about request boundaries allow an attacker to smuggle requests across users or bypass intended authorization checks.

Basic Auth in Axum is typically implemented by inspecting the Authorization header early in the middleware or route layer. If the header is parsed in a lenient way—such as accepting multiple header lines, tolerating ambiguous chunked encodings, or not strictly validating the request body termination—an upstream component (like a CDN or API gateway) may interpret the request differently than Axum. For example, a request that includes both an Authorization header and a body that also contains newline-like sequences can be parsed differently by the proxy and by Axum, enabling an attacker to inject a second request that is processed in the context of the first user’s authentication.

Consider an Axum service that uses a custom Basic Auth middleware and forwards the request internally without normalizing the Authorization header before passing it along. If the proxy terminates TLS and normalizes headers before reaching Axum, but Axum re-parses the header without validating that it has already been processed, an attacker can craft a request where the smuggled request’s Authorization header is interpreted differently. This can lead to privilege escalation (e.g., a low-privilege user gaining access to another user’s data) or unauthorized access to endpoints that rely on the parsed credentials.

One concrete pattern involves an Axum extractor that eagerly consumes the authorization header and then conditionally allows the request body to be processed without ensuring the request has not been split. For instance, if a middleware reads the header, decides authentication succeeded, and then passes the request to a handler that parses JSON or form data without validating content length or transfer encoding, an attacker can embed a second request within the body of the first. Because the proxy may forward the first request normally but Axum processes the second embedded request with the same authentication context, the smuggled request can execute under the permissions of the victim user.

Real-world attack patterns mirror known CVEs in other frameworks, such as request smuggling via mismatched handling of Transfer-Encoding and Content-Length. Even without those specific headers, custom Basic Auth logic that does not enforce strict header normalization and request boundary validation can create a similar vulnerability surface. The risk is especially pronounced when the application assumes the proxy will always normalize requests before they reach Axum, while Axum still performs its own parsing of authentication data.

To detect this class of issue, scanners perform black-box tests that send requests with ambiguous headers and body combinations, then inspect whether the response reflects processing of a second, smuggled request. Findings typically highlight missing normalization between the proxy and Axum, and emphasize the need to treat the Authorization header as authoritative only after validating the overall request structure.

Basic Auth-Specific Remediation in Axum — concrete code fixes

Remediation focuses on strict header normalization, avoiding dual parsing, and ensuring the request body is never interpreted differently after authentication has been established. Below are concrete Axum patterns that reduce the risk of request smuggling when using Basic Auth.

1. Use a strict, single source of authentication

Parse the Authorization header once, fail closed if it is malformed, and do not re-parse it later in the request lifecycle. Avoid extracting credentials in multiple layers or middlewares that may be independently configured.

use axum::{async_trait, extract::FromRequestParts, http::request::Parts};
use std::convert::Infallible;
use base64::Engine;

struct AuthenticatedUser(String);

#[async_trait]
impl FromRequestParts for AuthenticatedUser
where
    S: Send + Sync,
{
    type Rejection = (axum::http::StatusCode, String);

    async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result {
        let auth_header = parts.headers.get("authorization")
            .ok_or((axum::http::StatusCode::UNAUTHORIZED, "Missing authorization header".to_string()))?;
        let auth_str = auth_header.to_str().map_err(|_| (axum::http::StatusCode::UNAUTHORIZED, "Invalid header encoding".to_string()))?;
        if !auth_str.starts_with("Basic ") {
            return Err((axum::http::StatusCode::UNAUTHORIZED, "Invalid auth type".to_string()));
        }
        let encoded = auth_str[6..].trim();
        let decoded = base64::engine::general_purpose::STANDARD.decode(encoded)
            .map_err(|_| (axum::http::StatusCode::UNAUTHORIZED, "Invalid base64"))?;
        let creds = String::from_utf8(decoded).map_err(|_| (axum::http::StatusCode::UNAUTHORIZED, "Invalid credentials format"))?;
        let parts: Vec<&str> = creds.splitn(2, ':').collect();
        if parts.len() != 2 || parts[0].is_empty() || parts[1].is_empty() {
            return Err((axum::http::StatusCode::UNAUTHORIZED, "Invalid credentials".to_string()));
        }
        // At this point, authentication succeeded; do not re-parse the header elsewhere.
        Ok(AuthenticatedUser(parts[0].to_string()))
    }
}

2. Reject requests with ambiguous transfer encodings

Explicitly reject requests that include both Transfer-Encoding and Content-Length, or that use chunked encoding when your proxy is expected to handle it. This prevents a proxy and Axum from interpreting the request body boundary differently.

use axum::{Extension, Json};
use axum::http::HeaderMap;

async fn validate_request_headers(headers: &HeaderMap) -> Result<(), (axum::http::StatusCode, String)> {
    let has_te = headers.contains_key("transfer-encoding");
    let has_cl = headers.contains_key("content-length");
    if has_te && has_cl {
        return Err((axum::http::StatusCode::BAD_REQUEST, "Ambiguous transfer encoding".to_string()));
    }
    if has_te {
        // If your deployment does not terminate chunked encoding before Axum, reject it.
        return Err((axum::http::StatusCode::BAD_REQUEST, "Transfer-Encoding not supported".to_string()));
    }
    Ok(())
}

async fn handler(
    Extension(user): Extension,
    Json(body): Json<serde_json::Value>,
    headers: Extension<HeaderMap>,
) -> Result<impl IntoResponse, (axum::http::StatusCode, String)> {
    validate_request_headers(&headers.0).await?;
    // Proceed with business logic
    Ok((axum::http::StatusCode::OK, "OK"))
}

3. Do not concatenate or modify the Authorization header before forwarding

If you must forward requests internally, ensure you do not append or rewrite the Authorization header based on body content or other mutable fields. Use a proxy that normalizes the request before it reaches Axum, and configure Axum to trust the authenticated identity without re-evaluating the header in a way that could be influenced by a smuggled body.

4. Example of a secure middleware that enforces header integrity

use axum::{Layer, middleware::{self, Next}};
use axum::extract::Request;
use std::future::Future;

struct AuthLayer;

impl Layer for AuthLayer {
    type Service = AuthMiddleware;

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

struct AuthMiddleware {
    inner: S,
}

impl<S, Fut> middleware::Layer for AuthMiddleware<S>
where
    S: tower_service::Service<Request, Response = middleware::ServiceResponse<Fut>> + Send + 'static,
    S::Future: Send + 'static,
    Fut: Future + Send + 'static,
{
    // Implementation details omitted for brevity
}

// In practice, prefer the FromRequestParts approach above and avoid
// re-parsing headers in middleware after authentication has succeeded.

These patterns ensure that authentication is evaluated against a canonical, normalized request, reducing the likelihood that a proxy and Axum will interpret the same request differently. They complement the scanner’s findings by enforcing strict header handling and avoiding dual parsing that could enable request smuggling.

Frequently Asked Questions

Can request smuggling via Basic Auth allow an attacker to bypass authentication entirely?
Yes, if Axum and the upstream proxy interpret request boundaries differently, a smuggled request may be processed with the victim’s authenticated context, effectively bypassing intended authorization for that user.
Does enabling HTTPS prevent request smuggling in Axum with Basic Auth?
No. HTTPS protects confidentiality and integrity in transit, but request smuggling is a protocol-level issue about how requests are parsed. Proper header normalization and avoiding dual parsing in Axum are required regardless of TLS.