HIGH cross site request forgeryaxumbasic auth

Cross Site Request Forgery in Axum with Basic Auth

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

Cross Site Request Forgery (CSRF) in Axum with HTTP Basic Auth is concerning because Basic Auth credentials are sent with every request automatically by browsers when a user is "logged in" via an auth mechanism that does not use anti-CSRF protections. In a typical web flow, if a browser has cached Basic Auth credentials for a domain, any crafted HTML or script on another site can trigger requests to that domain using those cached credentials without user interaction or consent. This differs from token-based auth where browsers do not automatically attach credentials unless explicitly programmed to do so.

For Axum, which is a Rust web framework, CSRF with Basic Auth becomes a practical risk when endpoints rely solely on Basic Auth for authentication and lack additional CSRF mitigation such as same-site cookies, anti-CSRF tokens, or custom headers that cannot be set cross-origin. Since Basic Auth uses the Authorization header, a simple form or script on attacker.com can initiate a request to api.yourservice.com and the browser will include the Authorization header if the user has an active session. This enables unwanted state changes (money transfers, email updates, resource creation) on behalf of the authenticated user.

Consider an endpoint /api/v1/transfer that accepts POST with JSON body {"to": "attacker.com", "amount": 500}. If the endpoint only checks for valid Basic Auth credentials and does not validate origin or require a CSRF token, an attacker can host an image or script that triggers this POST. Because the browser automatically attaches the Basic Auth credentials, the server may process the transfer as if it were initiated by the legitimate user. This illustrates how the combination of a browser-managed auth mechanism (Basic Auth) and missing CSRF protections creates a bypass of intended user consent.

In black-box scanning terms, middleBrick checks for CSRF-like behaviors by analyzing whether authenticated endpoints accept state-changing methods without requiring a per-request nonce or origin verification, and whether exposed OpenAPI specs indicate missing security schemes for anti-CSRF. The scan does not exploit these issues but highlights where credentials are automatically persisted by the browser and where idempotent or unsafe methods lack additional authorization checks beyond the presence of auth.

It is important to note that Basic Auth over HTTPS protects credentials in transit, but does not protect against CSRF. Defense in depth is required: use anti-CSRF tokens, validate the Origin and Referer headers where appropriate, prefer same-site cookie attributes if using session cookies, and require custom headers (e.g., X-Requested-With) that cannot be set cross-origin by malicious sites. For APIs consumed by browsers, consider moving away from Basic Auth for session-like interactions and adopt token-based flows with explicit CSRF protections.

Basic Auth-Specific Remediation in Axum — concrete code fixes

To mitigate CSRF in Axum when using Basic Auth, implement explicit CSRF protections rather than relying on the absence of automatic credential transmission. Below are concrete code examples that show how to combine Basic Auth with anti-CSRF tokens and strict origin checks.

1) Basic Auth middleware with CSRF token validation

Use Axum extractors to validate both Basic Auth credentials and a CSRF token present in a custom header for state-changing operations. This ensures that even if a browser automatically supplies credentials, the request must also include a token that an attacker cannot read or guess.

use axum::{routing::post, Router, extract::State, http::HeaderMap};
use std::net::SocketAddr;
use serde::{Deserialize, Serialize};

#[derive(Clone)]
struct AppState {
    csrf_tokens: std::sync::Arc<std::sync::Mutex<std::collections::HashSet<String>>>,
}

async fn validate_csrf(
    headers: &HeaderMap,
    state: &State<AppState>
) -> Result<(), (axum::http::StatusCode, String)> {
    let token = headers.get("X-CSRF-Token")
        .and_treq::header::HeaderValue::to_str
        .ok_or_else(|| (axum::http::StatusCode::FORBIDDEN, "Missing CSRF token".to_string()))?;
    let mut tokens = state.csrf_tokens.lock().unwrap();
    if tokens.remove(token) {
        Ok(())
    } else {
        Err((axum::http::StatusCode::FORBIDDEN, "Invalid or used CSRF token".to_string()))
    }
}

async fn transfer_handler(
    State(state): State<AppState>,
    headers: HeaderMap,
    body: String,
) -> Result<axum::Json<()>, (axum::http::StatusCode, String)> {
    // Validate CSRF token for sensitive operations
    validate_csrf(&headers, &State(state)).await?;
    // Parse and validate body: {"to": "...", "amount": 500}
    let data: TransferBody = serde_json::from_str(&body)
        .map_err(|_| (axum::http::StatusCode::BAD_REQUEST, "Invalid JSON".to_string()))?;
    // Perform transfer logic here
    Ok(axum::Json(()))
}

#[derive(Deserialize)]
struct TransferBody {
    to: String,
    amount: u64,
}

async fn generate_csrf_token() -> String {
    use rand::Rng;
    let token: String = (0..32).map(|_| rand::random::()).collect();
    // In practice store token in state.csrf_tokens and return to client
    token
}

async fn handler() {}

#[tokio::main]
async fn main() {
    let state = AppState {
        csrf_tokens: std::sync::Arc::new(std::sync::Mutex::new(std::collections::HashSet::new())),
    };
    let app = Router::new()
        .route("/api/v1/transfer", post(transfer_handler))
        .with_state(state);
    let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
    axum::Server::bind(&addr).serve(app.into_make_service()).await.unwrap();
}

2) Require custom header for state-changing methods

Browsers cannot set arbitrary headers like X-Requested-With from cross-origin HTML forms or scripts due to CORS restrictions. Require such a header for POST/PUT/DELETE/PATCH on sensitive endpoints while still allowing Basic Auth for credential transport.

use axum::{routing::post, Router, extract::State, http::HeaderMap, async_trait};
use std::net::SocketAddr;

async fn require_custom_header(
    headers: &HeaderMap,
) -> Result<(), (axum::http::StatusCode, String)> {
    headers.get("X-Requested-With")
        .and_treq::header::HeaderValue::to_str
        .map(|s| if s == "XMLHttpRequest" { Ok(()) } else { Err("Invalid header") })
        .unwrap_or(Err("Missing X-Requested-With".to_string()))
        .map_err(|_| (axum::http::StatusCode::FORBIDDEN, "Missing required header".to_string()))
}

async fn sensitive_handler(
    headers: HeaderMap,
    body: String,
) -> Result<axum::Json<()>, (axum::http::StatusCode, String)> {
    require_custom_header(&headers).await?;
    // Process request knowing it originated from same-site JS that set the header
    Ok(axum::Json(()))
}

async fn handler() {}

#[tokio::main]
async fn main() {
    let app = Router::new()
        .route("/api/v1/admin", post(sensitive_handler))
        .route("/api/v1/public", post(handler));
    let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
    axum::Server::bind(&addr).serve(app.into_make_service()).await.unwrap();
}

3) Combine with same-site policies and preflight checks

While not Axum-specific, ensure your frontend sets cookies with SameSite=Strict or SameSite=Lax where applicable, and that CORS is configured to allow only trusted origins. For APIs intended for browser-based clients, include CORS middleware that restricts origins and does not allow credentials indiscriminately.

By requiring both valid Basic Auth credentials and a server-issued CSRF token (or a same-site enforced custom header), you mitigate CSRF risks even when Basic Auth credentials are automatically included by the browser. middleBrick scans can help identify endpoints that accept unsafe methods without these protections, enabling you to tighten your defenses.

Frequently Asked Questions

Does using Basic Auth in Axum automatically protect against CSRF?
No. Basic Auth credentials are automatically included by browsers when cached for a domain, which enables CSRF attacks. You must add explicit CSRF protections such as anti-CSRF tokens or strict custom headers that cannot be set cross-origin.
Can middleBrick detect CSRF risks related to Basic Auth in Axum APIs?
Yes. middleBrick scans analyze whether authenticated endpoints rely only on auth headers without additional CSRF mitigations, and it reports findings with remediation guidance. Note that middleBrick detects and reports; it does not fix or block requests.