HIGH insecure direct object referenceaxumjwt tokens

Insecure Direct Object Reference in Axum with Jwt Tokens

Insecure Direct Object Reference in Axum with Jwt Tokens — how this combination creates or exposes the vulnerability

Insecure Direct Object Reference (BOLA/IDOR) occurs when an API exposes internal resource identifiers and fails to enforce that the authenticated subject is authorized to access the specific object. In Axum, this commonly arises when an endpoint uses a path or query parameter (e.g., /users/123/profile) and only validates the presence of a JWT token without verifying that the token’s subject (sub claim) matches the requested resource.

JWT tokens typically carry claims such as subject (sub), roles, and scopes. If Axum routes use token claims for authorization only at a coarse level (e.g., "user" role) but do not compare the sub claim to the resource identifier, an attacker can change the ID in the request. Because the API does not check whether the user identified by the token owns or is permitted to act on the target ID, the request succeeds and data belonging to other users is returned or modified.

Consider an endpoint GET /users/{user_id}/profile that expects a JWT in the Authorization header. The token contains sub: "alice" and scope: "read:profile". The route handler extracts user_id from the path, fetches the profile from the store, and returns it after confirming the token is valid. If the handler does not assert that user_id corresponds to the sub claim in the token, an attacker can simply change user_id to another user’s ID (e.g., /users/bob_id/profile) and access unauthorized data. This is a classic BOLA/IDOR: the object reference (user_id) is direct and not bound to the token’s identity.

In Axum, this can be compounded by how authorization checks are structured. For example, if you perform role-based checks (e.g., require "admin" role) but neglect instance-level checks (sub matches user_id), an attacker with a low-privilege token can still iterate over IDs. Even when JWT tokens are used for authentication, missing or incorrect mapping between token claims and domain identifiers turns authentication into a bypass for authorization, enabling horizontal privilege escalation.

Real-world attack patterns include enumeration of user IDs via slightly different responses or timing differences, and exploitation of predictable numeric IDs. While OWASP API Top 10 lists BOLA/IDOR as a major risk, the specific interaction with JWT-based authentication in frameworks like Axum requires deliberate checks to ensure that every resource access validates the token’s subject against the requested resource identifier.

Jwt Tokens-Specific Remediation in Axum — concrete code fixes

To remediate BOLA/IDOR in Axum when using JWT tokens, bind the token’s subject claim to the resource identifier on every request. This means that after validating the token, the handler must compare the ID in the path/query with the sub claim and reject the request if they do not match (unless the caller has elevated permissions that are explicitly verified).

Below is a concrete Axum example that demonstrates secure handling. The example uses jsonwebtoken for decoding, axum extractors, and tower_http for tracing. It shows how to extract claims, enforce sub-to-id mapping, and return 403 when unauthorized.

use axum::{routing::get, Router, extract::State, http::StatusCode};
use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation, TokenData};
use serde::{Deserialize, Serialize};
use std::net::SocketAddr;

#[derive(Debug, Serialize, Deserialize)]
struct Claims {
    sub: String,
    roles: Vec<String>,
    exp: usize,
}

async fn profile_handler(
    State(state): State<AppState>,
    token_header: Option<axum::extract::Header<axum::http::header::AUTHORIZATION>>,
    Path(user_id): Path<String>,
) -> Result<impl IntoResponse, (StatusCode, String)> {
    let token = token_header
        .ok_or((StatusCode::UNAUTHORIZED, "Missing Authorization header".to_string()))?
        .0
        .to_str()
        .map_err(|_| (StatusCode::UNAUTHORIZED, "Invalid header encoding".to_string()))?
        .strip_prefix("Bearer ")
        .ok_or((StatusCode::UNAUTHORIZED, "Invalid Authorization format".to_string()))?;

    // Decode and validate token (use proper key management in production)
    let token_data: TokenData<Claims> = decode(
        token,
        &DecodingKey::from_secret("secret".as_ref()),
        &Validation::new(Algorithm::HS256),
    ).map_err(|_| (StatusCode::UNAUTHORIZED, "Invalid token".to_string()))?;

    // Critical BOLA/IDOR check: ensure the token subject matches the requested user_id
    if token_data.claims.sub != user_id {
        return Err((StatusCode::FORBIDDEN, "Access denied: subject mismatch".to_string()));
    }

    // Proceed to fetch profile for user_id, knowing sub matches
    let profile = state
        .user_store
        .get_profile(&user_id)
        .ok_or((StatusCode::NOT_FOUND, "Profile not found".to_string()))?;

    Ok(axum::Json(profile))
}

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

    let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
    axum::Server::bind(&addr)
        .serve(app.into_make_service())
        .await
        .unwrap();
}

In this example, the JWT token is decoded and the sub claim is explicitly compared to the :user_id path parameter. If they differ, a 403 response is returned, preventing unauthorized access. For endpoints that modify data, the same check should be performed before any write operation. Additionally, ensure token validation uses appropriate algorithms, key management, and verifies expiration (exp) and issuer (iss) as needed.

For more complex scenarios (e.g., admin users managing other users), implement a separate authorization layer that evaluates roles/scopes against the requested action, but always keep the sub-to-id mapping as the baseline for user-specific resources. Using middleware or request guards in Axum can centralize this check and reduce the risk of accidentally omitting it in individual handlers.

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 JWT tokens alone prevent BOLA/IDOR in Axum?
No. JWT tokens provide identity, but BOLA/IDOR requires explicit checks that the token's subject matches the resource identifier in each request. Authentication does not imply authorization for that specific object.
What additional steps help secure Axum APIs with JWT tokens?