HIGH bola idoraxumapi keys

Bola Idor in Axum with Api Keys

Bola Idor in Axum with Api Keys — how this specific combination creates or exposes the vulnerability

Broken Object Level Authorization (BOLA) occurs when an API fails to enforce proper ownership or authorization checks between a subject (e.g., a user or service identity) and the object they are attempting to access. In Axum, a common pattern is to look up a resource (such as a user profile, document, or tenant record) by ID and return it if the request is authenticated with an API key. The vulnerability arises when the API key is used only for authentication (proving identity) but not for authorization (proving permission to access that specific resource).

Consider an endpoint like /users/{user_id} that is protected by an API key header. If the handler retrieves the user_id from the path, queries the database for that ID, and returns the record without verifying that the authenticated principal (via the API key) owns or is allowed to view that user, BOLA exists. An attacker who knows or guesses another user’s ID can change the path parameter and access or modify data across accounts, even though every request presents a valid API key.

In Axum, this often maps to routes where a key extractor supplies an API key, but the handler does not cross-check the key’s associated scopes, roles, or allowed resource identifiers with the requested resource. For example, an API key scoped to tenant A might be accepted while the handler fetches data for tenant B because the handler trusts the client-supplied ID alone. This is a classic BOLA/IDOR flaw: authentication succeeds, but object-level authorization is missing or incomplete.

Real-world analogs in OWASP API Top 10 include excessive data exposure and broken access control. Attack patterns such as IDOR via predictable numeric IDs are common when developers rely on path parameters without contextual authorization. In regulated environments, this can lead to unauthorized access to sensitive data, violating principles like least privilege and failing expectations around tenant isolation.

Api Keys-Specific Remediation in Axum — concrete code fixes

To fix BOLA in Axum when using API keys, ensure that each request validates not only the presence of a key but also that the key permits access to the specific object requested. This typically involves binding the API key to a principal that includes allowed resource identifiers or scopes, and enforcing those constraints in the handler.

Below is a concrete, idiomatic Axum example that demonstrates secure handling. The API key is validated via a extractor that enforces key-to-permission mapping, and the handler cross-checks permissions before accessing the requested resource.

use axum::{routing::get, Router, extract::State, http::Request, response::IntoResponse};
use std::collections::HashMap;
use std::sync::Arc;

// Minimal domain models
#[derive(Clone, Debug)]
struct ApiKey {
    key_id: String,
    scopes: Vec, // e.g., ["users:read:123", "users:read:456"]
}

#[derive(Clone, Debug)]
struct User {
    id: u64,
    tenant_id: String,
    // other fields omitted
}

#[derive(Clone)]
struct AppState {
    // Simulated datastore
    users: Arc>,
    // Key registry: key string -> metadata
    keys: Arc>,
}

// Extract and validate API key, returning associated metadata
async fn api_key_auth(
    request: Request,
) -> Result {
    let headers = request.headers();
    let key = headers.get("X-API-Key")
        .and_then(|v| v.to_str().ok())
        .ok_or((axum::http::StatusCode::UNAUTHORIZED, "Missing API key".to_string()))?;
    // In practice, validate signature/issuer and fetch metadata from secure store
    // Here we simulate lookup
    unimplemented!("Validate key and fetch ApiKey securely")
}

// Authorization helper: check whether the key permits access to the requested user id
fn can_access_key(key: &ApiKey, user_id: u64) -> bool {
    key.scopes.iter().any(|s| s == &format!("users:read:{user_id}"))
}

async fn get_user_handler(
    State(state): State>,
    user_id: axum::extract::Path,
    request: Request,
) -> impl IntoResponse {
    let key = match api_key_auth(request).await {
        Ok(k) => k,
        Err(e) => return e,
    };
    let user_id = user_id.into_inner();
    let user = match state.users.get(&user_id) {
        Some(u) => u,
        None => return (axum::http::StatusCode::NOT_FOUND, "User not found").into_response(),
    };
    // BOLA prevention: ensure the key can access this specific user
    if !can_access_key(&key, user.id) {
        return (axum::http::StatusCode::FORBIDDEN, "Insufficient permissions".to_string()).into_response();
    }
    // Safe to return user data
    (
        axum::http::StatusCode::OK,
        format!("User {{ id: {}, tenant_id: {} }}", user.id, user.tenant_id),
    ).into_response()
}

#[tokio::main]
async fn main() {
    // Example setup (in practice, load securely)
    let mut users = HashMap::new();
    users.insert(1u64, User { id: 1, tenant_id: "t-abc".to_string() });
    let mut keys = HashMap::new();
    keys.insert("valid_key_1".to_string(), ApiKey { key_id: "valid_key_1".to_string(), scopes: vec!["users:read:1".to_string()] });
    keys.insert("valid_key_2".to_string(), ApiKey { key_id: "valid_key_2".to_string(), scopes: vec!["users:read:2".to_string()] });

    let app = Router::new()
        .route("/users/:user_id", get(get_user_handler))
        .with_state(Arc::new(AppState { users: Arc::new(users), keys: Arc::new(keys) }));

    // axum::Server::bind(&"0.0.0.0:3000".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
}

Key points in this remediation:

  • Authentication (API key validation) is separated from authorization (scope-to-resource checks).
  • The handler explicitly verifies that the authenticated key’s scopes include the specific user ID being requested, preventing BOLA regardless of the path parameter supplied by the client.
  • In production, replace the placeholder key validation with a secure lookup and signature verification, and store scopes in a way that supports efficient enforcement (e.g., per-tenant permissions, structured claims).

Additionally, consider design alternatives that reduce BOLA risk: use opaque identifiers instead of predictable numeric IDs, enforce tenant context in queries, and scope API keys to narrow resource sets. middleBrick can help identify such misconfigurations by scanning your Axum endpoints and mapping findings to frameworks like OWASP API Top 10.

Related CWEs: bolaAuthorization

CWE IDNameSeverity
CWE-250Execution with Unnecessary Privileges HIGH
CWE-639Insecure Direct Object Reference CRITICAL
CWE-732Incorrect Permission Assignment HIGH

Frequently Asked Questions

Can BOLA occur even when API keys are rotated regularly?
Yes. Regular rotation limits the impact of a leaked key but does not prevent BOLA if handlers do not enforce object-level authorization. An attacker who obtains a valid key can still iterate over allowed IDs unless each request is scoped to the specific resource.
Does enabling rate limiting prevent BOLA?
No. Rate limiting reduces abuse speed and volume but does not stop an authenticated attacker from accessing other valid objects they are not permitted to see. Authorization checks must validate the relationship between the key and the requested object.