Insecure Direct Object Reference in Actix with Bearer Tokens
Insecure Direct Object Reference in Actix with Bearer Tokens — how this combination creates or exposes the vulnerability
Insecure Direct Object Reference (BOLA/IDOR) occurs when an API exposes internal object references (e.g., database IDs) without verifying that the requesting actor has permission to access them. In Actix-web, this commonly arises when endpoints use path or query parameters to reference resources (such as /users/{user_id}/profile) and only check that a Bearer token is present, not whether the token’s subject is allowed to access the specific resource.
Consider an Actix endpoint that retrieves a user profile using a numeric ID from the URL and an access token in the Authorization header. If the handler extracts the token, confirms it is well-formed, and then directly loads the profile by ID without confirming ownership or authorization, the endpoint is vulnerable:
use actix_web::{web, HttpResponse, Error};
use serde::Deserialize;
#[derive(Deserialize)]
struct PathParams {
user_id: u64,
}
async fn get_profile(
params: web::Query,
req: actix_web::HttpRequest,
) -> Result<HttpResponse, Error> {
// Extract Bearer token
let token = match req.headers().get("Authorization") {
Some(h) => h.to_str().unwrap_or(""),
None => return Ok(HttpResponse::Unauthorized().finish()),
};
if !token.starts_with("Bearer ") {
return Ok(HttpResponse::BadRequest().body("Invalid auth scheme"));
}
// Vulnerable: no check that the authenticated subject owns or is allowed to access user_id
let profile = fetch_user_from_db(params.user_id).await;
Ok(HttpResponse::Ok().json(profile))
}
In this pattern, the Bearer token is used only for presence checks (e.g., authentication), but the handler does not enforce authorization scoped to the resource identified by user_id. An attacker who can guess or enumerate numeric IDs (or use leaked references) can read, modify, or delete other users’ data simply by supplying a valid Bearer token belonging to any user.
Two aspects amplify the risk in this combination:
- Actix routes often expose identifiers directly in paths, making it easy to manipulate references (e.g., changing
user_id=123touser_id=124). - Bearer tokens typically identify the actor (subject), but do not carry object-level permissions. Relying solely on the token without additional checks conflates authentication with authorization, which is a common root cause of IDOR in resource-based APIs.
Other IDOR-prone patterns include using internal surrogate keys (e.g., sequential invoice IDs) in URLs while failing to confirm that the authenticated principal is associated with the target record. MiddleBrick’s scans detect this by correlating the presence of Bearer token usage with endpoints that accept direct object references and lack complementary authorization checks.
Bearer Tokens-Specific Remediation in Actix — concrete code fixes
Remediation centers on ensuring that for every direct object reference, the server verifies that the authenticated subject (derived from the Bearer token) has the necessary rights to the requested resource. Below are concrete, idiomatic Actix examples that implement this principle.
1. Include subject ownership in the authorization check: After validating the Bearer token, decode the subject claim (e.g., sub or a custom user identifier) and compare it with the resource owner before proceeding.
use actix_web::{web, HttpResponse, Error, dev::ServiceRequest};
use actix_web::http::header::HeaderValue;
use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation};
use serde::{Deserialize, Serialize};
#[derive(Debug, Deserialize)]
struct Claims {
sub: String,
// include other standard or custom claims as needed
}
#[derive(Deserialize)]
struct PathParams {
user_id: u64,
}
async fn get_profile(
params: web::Query<PathParams>,
req: actix_web::HttpRequest,
) -> Result<HttpResponse, Error> {
// Extract and validate Bearer token
let auth = req.headers().get("Authorization")
.and_then(|v| v.to_str().ok())
.filter(|s| s.starts_with("Bearer "))
.map(|s| &s[7..])
.ok_or_else(|| actix_web::error::ErrorUnauthorized("Invalid or missing Bearer token"))?;
// Decode token and extract subject
let token_data = decode::
This pattern decodes the JWT Bearer token, extracts the subject, and compares it to the resource’s owner, ensuring that users can only access their own data when IDs are directly referenced.
2. Use indirect references or mapping tables instead of raw IDs: Where possible, avoid exposing sequential or guessable IDs. Use UUIDs or map internal IDs to opaque references verified against an ownership table.
use actix_web::{web, HttpResponse, Error};
use uuid::Uuid;
#[derive(Deserialize)]
struct PathParams {
profile_key: String, // opaque reference (e.g., UUID)
}
async fn get_profile_by_key(
params: web::Query<PathParams>,
req: actix_web::HttpRequest,
) -> Result<HttpResponse, Error> {
let auth = req.headers().get("Authorization")
.and_then(|v| v.to_str().ok())
.filter(|s| s.starts_with("Bearer "))
.map(|s| &s[7..])
.ok_or_else(|| actix_web::error::ErrorUnauthorized("Invalid or missing Bearer token"))?;
// Decode token as above to obtain subject (omitted for brevity)
let subject = decode_bearer_subject(auth).await;
// Map opaque key to a record and verify ownership
let record = fetch_record_by_key(¶ms.profile_key).await
.ok_or_else(|| actix_web::error::ErrorNotFound("Resource not found"))?;
if record.owner_id != subject {
return Err(actix_web::error::ErrorForbidden("Access denied"));
}
Ok(HttpResponse::Ok().json(record))
}
By combining Bearer token validation with per-resource authorization checks and avoiding direct exposure of mutable internal identifiers, you mitigate IDOR risks while preserving the utility of token-based authentication in Actix services.
Related CWEs: bolaAuthorization
| CWE ID | Name | Severity |
|---|---|---|
| CWE-250 | Execution with Unnecessary Privileges | HIGH |
| CWE-639 | Insecure Direct Object Reference | CRITICAL |
| CWE-732 | Incorrect Permission Assignment | HIGH |