HIGH time of check time of useaxumapi keys

Time Of Check Time Of Use in Axum with Api Keys

Time Of Check Time Of Use in Axum with Api Keys — how this specific combination creates or exposes the vulnerability

Time Of Check Time Of Use (TOCTOU) occurs when the outcome of a security decision depends on the timing between a check and the subsequent use of the resource. In Axum, combining API key validation with downstream logic can introduce TOCTOU when the key is verified once but permissions or validity change before the request is fully processed.

Consider an endpoint that first checks the presence and validity of an API key, then loads user-specific permissions or a mutable resource (e.g., a subscription record from a database). If the key check passes but the user’s permissions are altered or the key is revoked between the check and the actual business logic, the request may execute with outdated authorization. This gap exists because the authorization check and the action are not atomic and may rely on mutable state external to the key validation step.

In Axum, a typical pattern looks like:

async fn handler(
    Key(api_key) = State(<ApiKeyStore>),
    // ... other extractors
) -> impl IntoResponse {
    // 1) Check API key validity (TOCTOU gap introduced here)
    let key_record = db.get_key(&api_key).await?;
    if !key_record.is_valid() {
        return Err(ApiKeyError); // 401
    }
    // 2) Later use: fetch mutable user state or perform an action
    let user = db.get_user_permissions(&key_record.user_id).await?;
    if !user.can("export_data") {
        return Err(PermissionError); // 403
    }
    // Proceed with export logic
    Ok(()) 
}

The vulnerability is that key_record.is_valid() and user.can(...) rely on data that may change after the key check. An attacker could revoke or rotate the key after the check but before the permission check or data export, bypassing intended restrictions. This mirrors the broader OWASP API Top 10 category:2023-A01:2023 Broken Access Control, where authorization logic is not enforced consistently at the point of use.

Additionally, if the API key is cached or stored in a shared in-memory store and another process updates the key’s status, the runtime behavior becomes dependent on timing and concurrency. Without synchronizing the check and use (e.g., re-validating within the same transactional boundary), the endpoint remains susceptible to privilege escalation or unauthorized data access.

middleBrick’s scans include checks for BOLA/IDOR and BFLA/Privilege Escalation, which can surface these authorization timing gaps when scanning an Axum endpoint that relies on API keys. Findings map to compliance frameworks such as OWASP API Top 10 and SOC2, emphasizing the need to tighten authorization around the point of use.

Api Keys-Specific Remediation in Axum — concrete code fixes

To prevent TOCTOU with API keys in Axum, couple validation and usage within a single, consistent authorization step and avoid mutable external state between check and use. Prefer verifying the key and fetching permissions together, ideally within a database transaction or by embedding necessary claims directly in the key material.

Remediation strategies include:

  • Validate and load permissions atomically.
  • Use short-lived keys and avoid mutable permission checks after validation.
  • Embed minimal required claims in the key or JWT so that authorization can be enforced locally without additional mutable lookups.

Example 1 — atomic validation and permissions fetch within a single async block (still dependent on DB consistency; use transactions where supported):

async fn handler(
    Key(api_key) = State(<ApiKeyStore>),
    Extension(db): Extension<Arc<DbPool>>,
) -> Result<impl IntoResponse, AppError> {
    let mut tx = db.begin().await?;
    // Fetch key and permissions together, ensuring consistency
    let key_record = sqlx::query_as!(KeyRecord, "SELECT * FROM api_keys WHERE key = $1", api_key)
        .fetch_optional(&mut *tx)
        .await?;
    let key = key_record.ok_or(AppError::Unauthorized)?;
    if !key.is_valid {
        return Err(AppError::Unauthorized);
    }
    let permissions: Vec<String> = sqlx::query_scalar("SELECT permission FROM permissions WHERE user_id = $1")
        .bind(key.user_id)
        .fetch_all(&mut *tx)
        .await?;
    tx.commit().await?;
    // Enforce authorization locally without extra mutable lookups
    if !permissions.contains("export_data") {
        return Err(AppError::Forbidden);
    }
    // Proceed
    Ok(()) 
}

Example 2 — embed claims in the API key payload (e.g., signed JWT) so validation requires no mutable lookup, eliminating the window between check and use:

use axum::extract::FromRequest;
use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation};

#[derive(Debug, Clone)]
struct ApiKeyClaims {
    pub sub: String,
    pub permissions: Vec<String>,
    pub exp: usize,
}

async fn handler(
    Key(claims): Extension<ApiKeyClaims>, // Extracted and validated via middleware
) -> impl IntoResponse {
    // Use claims.permissions directly; no mutable DB call at handler
    if !claims.permissions.contains("export_data") {
        return Err(ApiKeyError); // 401 or 403
    }
    // Proceed safely
    Ok(())
}

In both examples, the key validation and authorization decision are tightly bound, reducing the TOCTOU window. For ongoing protection, consider integrating middleBrick’s CLI to scan from terminal with middlebrick scan and review findings related to BOLA/IDOR and privilege escalation. Teams using CI/CD can add API security checks to their pipeline with the GitHub Action to fail builds if risk scores degrade.

Frequently Asked Questions

Why does checking API key validity and then checking permissions later create a security risk?
Because the key’s validity or the user’s permissions can change between the check and the use, leading to authorization decisions based on stale data. This is a Time Of Check Time Of Use (TOCTOU) condition that can allow privilege escalation or unauthorized access.
Can embedding claims in the API key fully prevent TOCTOU in Axum?
Embedding claims reduces TOCTOU by removing mutable lookups after validation, but you must ensure the key material is securely signed, short-lived, and rotated. Always pair this with transport security and revocation mechanisms for defense in depth.