Insecure Direct Object Reference in Fastapi with Jwt Tokens
Insecure Direct Object Reference in Fastapi with Jwt Tokens — how this specific combination creates or exposes the vulnerability
Insecure Direct Object Reference (BOLA/IDOR) occurs when an API exposes internal object references such as numeric IDs or UUIDs and relies solely on the caller to be authenticated, without verifying that the authenticated subject has permission to access the specific object. In Fastapi, using JWT tokens for authentication can inadvertently enable IDOR when authorization checks are incomplete or applied inconsistently across endpoints.
Consider an endpoint that retrieves user profiles using a path parameter like /users/{user_id}. Authentication is enforced by validating a JWT access token, but the token only identifies the subject (e.g., sub) without enforcing that the subject can only access their own data. If the implementation does not compare the decoded subject from the JWT with the requested user_id, an authenticated attacker can modify the path parameter to access other users’ profiles. This is a classic BOLA/IDOR: the application conflates authentication (who you are) with authorization (what you are allowed to do).
Fastapi applications often use scopes or roles encoded in the JWT to implement coarse-grained permissions, but if these claims are not explicitly checked against the resource ownership or tenant context, the attack surface remains open. For example, a JWT might include a role claim set to admin, and the endpoint might allow broader reads based on that role without confirming the specific resource belongs to the actor’s permitted set. Attackers can probe numeric or predictable IDs to enumerate records across other users or tenants, leading to mass data exposure.
Another common pattern is using opaque identifiers (e.g., UUIDs) in URLs while failing to map them to the correct owning subject in the database query. Even with JWT validation, if the query does not join on the subject identifier (e.g., WHERE user_id = :sub AND id = :path_id), the endpoint returns data for any valid ID. This becomes especially risky when using JWT tokens with long lifetimes or when refresh tokens are not tightly bound to access patterns, increasing the window for IDOR exploitation.
Tools like middleBrick detect this by running black-box scans that exercise endpoints with different authenticated contexts using JWT tokens. It compares responses across subjects and looks for missing ownership enforcement, excessive data exposure in JSON payloads, and whether predictable or guessable identifiers are accepted without per-request authorization checks. Findings include references to the OWASP API Top 10 (2023) A01:2027 — Broken Object Level Authorization and relevant compliance mappings such as SOC2 and GDPR, highlighting the need to bind resource access to the authenticated subject rather than relying on authentication alone.
Jwt Tokens-Specific Remediation in Fastapi — concrete code fixes
To remediate IDOR when using JWT tokens in Fastapi, always enforce resource ownership checks in the business logic layer, never rely on authentication or role claims alone. The following patterns demonstrate how to implement per-request authorization that ties JWT subject claims to the requested resource identifiers.
Example 1: Basic user-owned profile endpoint with JWT subject validation.
from fastapi import Depends, FastAPI, HTTPException, status
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from jose import jwt, JWTError
from pydantic import BaseModel
from typing import Optional
app = FastAPI()
security = HTTPBearer()
SECRET_KEY = "your-secret-key"
ALGORITHM = "HS256"
class ProfileResponse(BaseModel):
user_id: str
username: str
email: str
def get_current_subject(credentials: HTTPAuthorizationCredentials = Depends(security)):
token = credentials.credentials
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
subject: Optional[str] = payload.get("sub")
if subject is None:
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Missing subject")
return subject
except JWTError:
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token")
# Simulated database layer
def get_profile_from_db(user_id: str):
# Replace with actual DB query; here we simulate a record
if user_id == "user-123":
return {"user_id": "user-123", "username": "alice", "email": "[email protected]"}
return None
@app.get("/users/me", response_model=ProfileResponse)
async def read_own_profile(subject: str = Depends(get_current_subject)):
profile = get_profile_from_db(subject)
if profile is None:
raise HTTPException(status_code=404, detail="Profile not found")
return ProfileResponse(**profile)
This pattern decodes the JWT and extracts the sub claim, then passes it directly into the data access function. The endpoint only returns data when the database record matches the subject, ensuring that authentication (JWT) and authorization (ownership) are both enforced.
Example 2: Parameterized user endpoint with explicit ownership check.
@app.get("/users/{user_id}", response_model=ProfileResponse)
async def read_user(
user_id: str,
subject: str = Depends(get_current_subject)
):
if subject != user_id:
raise HTTPException(status_code=403, detail="Forbidden: cannot access other users’ data")
profile = get_profile_from_db(user_id)
if profile is None:
raise HTTPException(status_code=404, detail="Profile not found")
return ProfileResponse(**profile)
Here, the endpoint compares the JWT subject with the path parameter before querying the database. This explicit check prevents BOLA/IDOR by ensuring that users can only access resources they own, even when JWT tokens are valid and contain broader claims.
For roles-based access, include additional claims such as role or permissions in the authorization logic, but still bind them to the resource and request context. middleBrick’s scans verify that such checks exist across endpoints and flags missing constraints as high-severity findings with remediation guidance tied to OWASP API Top 10 and common compliance frameworks.
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 |