HIGH excessive data exposureaxumbasic auth

Excessive Data Exposure in Axum with Basic Auth

Excessive Data Exposure in Axum with Basic Auth — how this specific combination creates or exposes the vulnerability

Excessive Data Exposure occurs when an API returns more information than necessary for a given operation, and combining this with Basic Authentication in an Axum service can amplify the risk. Basic Authentication sends credentials in an Authorization header as base64-encoded username:password, which is easily reversible if not protected by TLS. In Axum, if responses include sensitive fields such as internal IDs, database keys, password hashes, or session tokens, an attacker who gains access to these responses—through logs, browser history, or network interception—can correlate that data with the reused credentials to escalate harm.

When middleware in Axum is configured to attach user information to request extensions for downstream handlers, developers may inadvertently serialize and return these extended attributes in JSON responses. For example, attaching a user ID extracted from Basic Auth into a struct that is serialized with Serde can expose internal identifiers if the API does not explicitly filter them. This becomes excessive data exposure when the API returns full user records, including fields like email, role, or pointers to related resources, without a need for the client to see them. Because Basic Auth lacks built-in mechanisms to scope or limit what data a client can infer, each request carries the same credential context, making it easier for an attacker to map data exposure across endpoints.

Another vector involves error responses. In Axum, custom error handlers may include debug details or stack traces that reference internal objects or database keys. If Basic Auth is used and those errors are returned with detailed context, they can disclose paths, table names, or business logic that an attacker can exploit. Since Basic Auth does not provide per-request tokens or scopes, there is no natural boundary preventing an attacker from probing multiple endpoints with the same credentials to map the data landscape. This repeated exposure across unauthenticated or weakly authenticated routes means that even without breaking TLS, an attacker can build a profile of what data exists and how it flows through the service.

middleBrick scans detect these patterns by correlating unauthenticated endpoint behavior with the presence of Basic Auth headers and identifying response fields that should be omitted. The scanner flags findings such as PII in responses, missing field-level filtering, verbose errors, and endpoints that return full resource objects when a subset would suffice. These findings map to OWASP API Top 10 A01:2023 Broken Object Level Authorization when combined with BOLA risks, and they highlight the need for explicit data shaping in Axum handlers and responses.

Basic Auth-Specific Remediation in Axum — concrete code fixes

To mitigate Excessive Data Exposure when using Basic Authentication in Axum, you should combine transport security, selective serialization, and strict handler design. Always enforce HTTPS so that Basic Auth credentials and responses are encrypted in transit. Then, design your response structs to include only the data necessary for the client, and avoid returning full domain models. Use extractor patterns that limit what user information is attached to requests, and ensure error handlers do not leak internal details.

Below are concrete Axum examples that demonstrate secure handling with Basic Auth.

1. Enforce HTTPS and validate credentials without overexposing data

use axum::{
    async_trait,
    extract::FromRequest,
    http::{self, Request},
    response::IntoResponse,
    routing::get,
    Router,
};
use std::net::SocketAddr;
use headers::authorization::{Authorization, Basic};

struct AuthenticatedUser {
    id: u64,
    username: String,
    // Do not include password_hash or sensitive internal fields here
}

#[async_trait]
impl FromRequest for AuthenticatedUser
where
    S: Send + Sync,
{
    type Rejection = (http::StatusCode, &'static str);

    async fn from_request(req: Request) -> Result {
        let auth_header = req.headers().get(http::header::AUTHORIZATION)
            .ok_or((http::StatusCode::UNAUTHORIZED, "missing authorization header"))?;
        let basic = auth_header.parse::>()
            .map_err(|_| (http::StatusCode::UNAUTHORIZED, "invalid authorization header"))?;
        let user = validate_basic_credentials(basic.user_id(), basic.password())
            .ok_or((http::StatusCode::UNAUTHORIZED, "invalid credentials"))?;
        Ok(user)
    }
}

fn validate_basic_credentials(user_id: &str, password: &str) -> Option {
    // Perform constant-time validation against a secure store
    if user_id == "alice" && password == "correcthorsebatterystaple" {
        Some(AuthenticatedUser {
            id: 1,
            username: "alice".to_string(),
        })
    } else {
        None
    }
}

async fn profile_handler(user: AuthenticatedUser) -> impl IntoResponse {
    // Return only the data needed by the client
    (http::StatusCode::OK, serde_json::json!({
        "id": user.id,
        "username": user.username,
    }))
}

#[tokio::main]
async fn main() {
    let app = Router::new()
        .route("/profile", get(profile_handler));

    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
    axum::serve(listener, app).await.unwrap();
}

2. Use a dedicated response model to avoid leaking fields

use axum::{
    extract::Extension,
    response::Json,
    routing::get,
    Router,
};
use serde::Serialize;

#[derive(Serialize)]
struct PublicProfile {
    id: u64,
    username: String,
    // Do not include email, role, or internal pointers
}

async fn get_public_profile(
    Extension(user): Extension,
) -> Json {
    let public = PublicProfile {
        id: user.id,
        username: user.username,
    };
    Json(public)
}

fn build_router() -> Router {
    Router::new()
        .route("/public/profile", get(get_public_profile))
        .layer(Extension(AuthenticatedUser { id: 1, username: "alice".to_string() }))
}

3. Secure error handling that avoids data exposure

use axum::{
    response::{IntoResponse, Response},
    Json,
};
use serde::Serialize;

#[derive(Serialize)]
struct ErrorResponse {
    message: String,
    // Never include stack traces or internal codes in production
}

async fn not_found_handler() -> Response {
    let error = ErrorResponse {
        message: "The requested resource was not found.".to_string(),
    };
    (http::StatusCode::NOT_FOUND, Json(error)).into_response()
}

By using these patterns in Axum, you reduce the attack surface associated with Basic Auth: credentials are protected in transit, responses contain only necessary data, and errors do not disclose internal state. middleBrick can validate these mitigations by checking for overly broad response objects, missing field filtering, and verbose error handling in unauthenticated scans.

Related CWEs: propertyAuthorization

CWE IDNameSeverity
CWE-915Mass Assignment HIGH

Frequently Asked Questions

Does middleBrick fix the vulnerabilities it finds in Axum APIs?
No. middleBrick detects and reports findings with remediation guidance, but it does not fix, patch, or block anything in your API.
Can I test my Axum API without sending credentials to middleBrick?
Yes. middleBrick runs unauthenticated scans by default; you can submit the endpoint URL without credentials and it will still identify excessive data exposure and other issues.