HIGH token leakageaxum

Token Leakage in Axum

How Token Leakage Manifests in Axum

Token leakage in Axum applications typically occurs through improper error handling and logging mechanisms. When authentication fails or middleware panics, sensitive tokens can be inadvertently exposed in error responses or log files. A common scenario involves Axum's built-in error handling middleware that captures panics and returns detailed error responses containing request context.

For example, consider an endpoint that extracts a token from the Authorization header:

async fn protected_endpoint(
    extract::Header('Authorization'): String,
) -> Result<String> {
    // Token processing logic
    Ok("Protected data".to_string())
}

If the token is malformed or missing, Axum's default error handling might return a response like:

{"error":"missing header 'Authorization'","details":{"header":"Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."}}

This exposes the token to any client making the request, including potential attackers. Another manifestation occurs in structured logging where middleware logs request details including headers:

async fn logging_middleware(
    req: Request,
    next: Next,
) -> Result<Response> {
    let headers = req.headers();
    log::info!("Processing request: {:?}", headers); // Logs Authorization header
    next.run(req).await
}

Middleware panics during token validation can also leak sensitive data. If a custom middleware panics while processing a JWT, Axum's panic handler might include the raw token in the error response:

async fn auth_middleware(
    req: Request,
    next: Next,
) -> Result<Response> {
    let auth_header = req.headers().get("Authorization").unwrap();
    let token = auth_header.to_str().unwrap();
    // Malformed token causes panic
    let claims: Claims = serde_json::from_str(token).unwrap();
    next.run(req).await
}

Additionally, Axum's extractors can inadvertently capture and expose tokens through debug representations or error messages when deserialization fails.

Axum-Specific Detection

Detecting token leakage in Axum requires examining both the application code and runtime behavior. Static analysis should focus on middleware implementations and error handling patterns. Look for extractors that directly consume Authorization headers without proper validation:

async fn find_token_leaks(app: &AxumApp) -> Vec<String> {
    let mut issues = Vec::new();
    
    // Check for direct header extraction without validation
    for route in app.routes() {
        if route.handler().uses_extractor("Authorization") {
            issues.push(format!("Potential token exposure in {}", route.path()));
        }
    }
    
    issues
}

Runtime detection involves monitoring responses for leaked tokens using regex patterns that match JWT and other token formats:

async fn monitor_for_leaks(
    client: &reqwest::Client,
    endpoint: &str,
) -> Result<Vec<String>> {
    let response = client.get(endpoint).send().await?;
    let body = response.text().await?;
    
    // JWT token pattern detection
    let jwt_pattern = r#"ey[\w-]+\.ey[\w-]+\.[\w-]+"#;
    let mut leaks = Vec::new();
    
    for cap in Regex::new(jwt_pattern).unwrap().find_iter(&body) {
        leaks.push(cap.as_str().to_string());
    }
    
    Ok(leaks)
}

middleBrick's black-box scanning approach is particularly effective for Axum applications since it tests the actual running service without requiring source code access. The scanner sends malformed requests to trigger error conditions and examines responses for token exposure:

middlebrick scan https://api.example.com/auth

This command tests unauthenticated endpoints and analyzes responses for leaked tokens, authentication bypass attempts, and other security issues specific to Axum's routing and middleware architecture.

Axum-Specific Remediation

Remediating token leakage in Axum requires implementing proper error handling and sanitization at multiple layers. Start with custom error handling middleware that prevents token exposure:

use axum_extra::routing::Router;
use axum::http::StatusCode;

async fn custom_error_handler(err: Error) -> Response {
    // Sanitize error details before sending response
    let sanitized = match err.kind() {
        ErrorKind::MissingHeader => Error::new("Authentication required"),
        _ => Error::new("Internal server error"),
    };
    
    let json = serde_json::json!({"error": sanitized.message()});
    let body = axum::body::Body::from(serde_json::to_vec(&json).unwrap());
    
    Response::builder()
        .status(StatusCode::BAD_REQUEST)
        .header("Content-Type", "application/json")
        .body(body)
        .unwrap()
}

let app = Router::new()
    .route("/api/*", get(protected_endpoint).post(create_item))
    .layer(axum::middleware::from_fn(custom_error_handler));

Implement structured logging with token redaction:

use axum::middleware::Next;
use axum::Request;

async fn logging_middleware(
    mut req: Request,
    next: Next,
) -> Result<Response> {
    // Remove sensitive headers before logging
    let mut headers = req.headers().clone();
    headers.remove("Authorization");
    headers.remove("Cookie");
    
    log::info!("Request: {} {}", req.method(), req.uri());
    
    let res = next.run(req).await?;
    
    // Log response without sensitive data
    log::info!("Response: {}", res.status());
    
    Ok(res)
}

Use Axum's type-safe extractors with proper validation:

#[derive(Deserialize)]
struct AuthHeader {
    #[serde(rename = "Authorization")]
    token: String,
}

async fn validate_token(
    extract::Json(AuthHeader): extract::Json<AuthHeader>,
) -> Result<impl Reply> {
    // Validate token format before processing
    if !is_valid_jwt_format(&auth_header.token) {
        return Err(axum::http::StatusCode::UNAUTHORIZED);
    }
    
    // Process token securely
    Ok("Authenticated".to_string())
}

fn is_valid_jwt_format(token: &str) -> bool {
    let parts: Vec<&str> = token.split('.').collect();
    parts.len() == 3 && parts.iter().all(|p| !p.is_empty())
}

Implement comprehensive testing to verify token leakage is prevented:

#[cfg(test)]
mod tests {
    use axum::http::StatusCode;
    
    #[tokio::test]
    async fn test_no_token_leakage() {
        let app = create_test_app();
        let client = reqwest::Client::new();
        
        // Test malformed token
        let res = client
            .get("http://localhost:3000/api/protected")
            .header("Authorization", "invalid.token")
            .send()
            .await
            .unwrap();
        
        assert_eq!(res.status(), StatusCode::UNAUTHORIZED);
        
        let body = res.text().await.unwrap();
        // Verify token not in response
        assert!(!body.contains("token"));
        assert!(!body.contains("ey")); // JWT prefix
    }
}

Frequently Asked Questions

How does Axum's default error handling contribute to token leakage?
Axum's default error handling captures panics and returns detailed error responses that may include request context, headers, and other sensitive data. When authentication middleware panics due to malformed tokens or missing headers, the error response can inadvertently expose the raw token or authentication details to the client.
Can middleBrick detect token leakage in Axum applications without source code access?
Yes, middleBrick's black-box scanning approach tests the running Axum service by sending various requests and analyzing responses. It looks for leaked tokens in error messages, logs, and response bodies without requiring access to the source code, making it ideal for testing production APIs.