HIGH memory leakaxumbasic auth

Memory Leak in Axum with Basic Auth

Memory Leak in Axum with Basic Auth — how this specific combination creates or exposes the vulnerability

A memory leak in an Axum service using HTTP Basic Authentication can occur when request-scoped data (such as decoded credentials) is retained beyond the lifetime of a single request. In Axum, handlers are typically async and operate over shared state, middleware, and extractors. If a developer stores decoded username/password values in a Arc>> or a similar long-lived structure without cleaning up, each authenticated request adds entries that are never removed. Over time, this increases the process heap size, leading to higher RSS, more frequent garbage collection pauses, and potential denial of service.

Basic Auth transmits credentials on every request, and Axum commonly decodes the header in an extractor or middleware. If that decoded data is cached—for example, to avoid re-parsing the header on subsequent middleware layers—but the cache key does not include the request context or is not bounded, entries accumulate. This is particularly risky when combined with high request volume or when tokens are not invalidated. Unlike token-based schemes where revocation can be centralized, Basic Auth credentials are often replayed, making unbounded caching or logging especially dangerous.

The combination of Axum’s flexible extractor model and Basic Auth’s stateless credential transmission means developers must be deliberate about lifetimes. Extractors like Extension and State encourage sharing, but if decoded credentials are placed into shared state without expiration or size limits, the attack surface grows. An attacker can induce memory growth by sending many authenticated requests with varying credentials, or by crafting requests that force repeated decoding and storage. This aligns with patterns seen in authentication-related issues cataloged in frameworks like OWASP API Top 10, where improper management of authentication data leads to resource exhaustion.

In an unauthenticated scan using middleBrick, such a leak might not be directly visible in a single 5–15 second run, but repeated probes against authenticated endpoints can expose trends in resource usage. middleBrick’s checks around Authentication, Input Validation, and Unsafe Consumption can surface misconfigurations that contribute to retention of sensitive data. Because middleBrick maps findings to frameworks like OWASP API Top 10 and provides remediation guidance, teams can identify unbounded caching or improper state handling early.

Instrumentation is essential to confirm a leak: observe process memory over time under load, compare heap profiles before and after sustained traffic, and audit middleware and extractor code for unbounded collections. If shared state is necessary, prefer bounded caches with TTL, avoid retaining raw credentials, and ensure extractors do not implicitly promote request-local data to application-wide storage.

Basic Auth-Specific Remediation in Axum — concrete code fixes

To prevent memory leaks when using Basic Auth in Axum, manage credential lifetimes strictly and avoid persisting decoded values in long-lived structures. Use request-local extraction and ensure any shared state is bounded and cleaned up. Below are concrete patterns and code examples.

First, prefer extracting credentials per request without storing them. An extractor can decode Basic Auth and return a lightweight struct that lives only for the handler:

use axum::{async_trait, extract::FromRequest, http::request::Parts, response::Response, TypedHeader};
use base64::Engine;
use std::convert::Infallible;

struct BasicAuth {
    pub username: String,
    pub password: String,
}

#[axum::async_trait]
impl FromRequest for BasicAuth
where
    S: Send + Sync,
{
    type Rejection = Response;

    async fn from_request(req: Parts, _state: &S) -> Result {
        let header = req.headers.get("authorization")
            .and_then(|v| v.to_str().ok())
            .ok_or_else(|| Response::builder().status(400).body("Missing auth".into()).unwrap())?;
        if !header.starts_with("Basic ") {
            return Err(Response::builder().status(400).body("Invalid auth type".into()).unwrap());
        }
        let encoded = header.trim_start_matches("Basic ");
        let decoded = base64::engine::general_purpose::STANDARD.decode(encoded)
            .map_err(|_| Response::builder().status(400).body("Invalid base64".into()).unwrap())?;
        let parts: Vec<&str> = std::str::from_utf8(&decoded)
            .map_err(|_| Response::builder().status(400).body("Invalid utf8".into()).unwrap())?
            .splitn(2, ':')
            .collect();
        if parts.len() != 2 {
            return Err(Response::builder().status(400).body("Invalid credentials".into()).unwrap());
        }
        Ok(BasicAuth {
            username: parts[0].to_string(),
            password: parts[1].to_string(),
        })
    }
}

async fn handler(Auth(credentials): Auth) -> &'static str {
    // Use credentials within the handler; they are dropped at the end of the request.
    if credentials.username == "admin" && credentials.password == "secret" {
        "ok"
    } else {
        "unauthorized"
    }
}

This approach avoids placing credentials into shared state. If you must cache or share data, use a bounded structure with TTL. For example, an moka cache keyed by a request-scoped identifier (rather than raw credentials) limits growth:

use axum::{Extension, Json};
use moka::sync::Cache;
use std::sync::Arc;

struct AppState {
    // Bounded cache: max 10_000 entries, expire after 30 minutes
    cache: Arc>,
}

async fn endpoint(
    Extension(state): Extension>,
    auth: Auth,
) -> Json<&'static str> {
    // Use a request-specific key, not the raw password
    let key = format!("{}:{}", auth.username, uuid::Uuid::new_v4());
    state.cache.insert(key, "some derived value".to_string());
    Json("ok")
}

Additionally, avoid logging or serializing raw Basic Auth headers. If you need to audit, hash or tokenize identifiers rather than storing usernames/passwords. Combine these practices with rate limiting and monitoring to reduce risk. With the Pro plan, continuous monitoring can alert you to abnormal memory growth on endpoints using authentication.

Frequently Asked Questions

Can middleBrick detect a memory leak in Axum with Basic Auth?
middleBrick can surface indicators such as Authentication misconfigurations and Unsafe Consumption findings that may contribute to retention issues, but it does not directly measure process memory growth. Use runtime profiling and heap analysis to confirm a leak.
Does Basic Auth inherently cause memory leaks in Axum?
No, Basic Auth does not inherently cause leaks. Leaks arise from how credential data is stored and retained. Avoid unbounded caches and ensure extractors do not promote request-local data to long-lived state.