Insecure Direct Object Reference in Fastapi with Mongodb
Insecure Direct Object Reference in Fastapi with Mongodb — 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 database IDs in URLs and fails to enforce that the requesting user is authorized to access the corresponding resource. Fastapi, with its dependency injection and route parameter handling, makes it straightforward to bind URL path parameters directly to database queries. When those queries use a user-supplied identifier without verifying authorization against the current subject, the endpoint becomes vulnerable.
Consider a Fastapi service that stores user data in a MongoDB collection. A route like /users/{user_id} can directly map user_id to a MongoDB _id lookup. If the handler runs db["users"].find_one({"_id": user_id}) without confirming that the authenticated subject owns or is permitted to view that document, any attacker who guesses or enumerates valid _id values can read other users’ records. This pattern is common when developers trust the client-supplied identifier and do not couple the query with the actor’s identity or tenant context.
In a typical unauthenticated or partially authenticated scenario, an attacker may not need credentials to exploit IDOR if the endpoint relies solely on object ownership checks that are incomplete. For example, an endpoint that returns user_id-scoped data might be invoked with a tampered user_id while the API still returns success because the handler never cross-checks the subject’s permissions. MiddleBrick’s BOLA/IDOR checks are designed to detect such weaknesses by correlating spec-defined parameters with runtime authorization behavior, highlighting endpoints where identifiers are used without proper constraints.
Fastapi’s OpenAPI generation can inadvertently document parameters in a way that suggests object-level access control without actually enforcing it. If the spec describes user_id as a path parameter but the implementation does not validate scope, the unauthenticated attack surface includes guessing valid identifiers. MongoDB’s use of _id values that are often sequential or UUID-based can make enumeration feasible. Thus, the combination of a flexible router, direct parameter-to-query mapping, and missing authorization logic creates a clear IDOR vector that scans like middleBrick can surface through runtime probing and spec analysis.
Mongodb-Specific Remediation in Fastapi — concrete code fixes
To mitigate IDOR when Fastapi interacts with MongoDB, couple every data access with the actor’s identity or tenant context and enforce least privilege at the query level. Avoid using raw user-supplied identifiers as the sole lookup key. Instead, bind the request subject to the query so that even a guessed identifier cannot retrieve unauthorized documents.
Below are concrete, working code examples that demonstrate secure patterns. The first shows a protected endpoint that includes the authenticated subject in the MongoDB filter. The second illustrates scoping by tenant or ownership field, which is essential in multi-tenant designs.
from fastapi import Depends, FastApi, HTTPException, status
from pymongo import MongoClient
from bson import ObjectId
import uuid
app = FastApi()
client = MongoClient("mongodb://localhost:27017")
db = client["secure_app"]
users = db["users"]
# Assume a dependency that provides the authenticated subject’s user_id
def get_current_subject() -> str:
# In practice, this would validate tokens/sessions and return a user_id
return "subject-uuid-here"
@app.get("/users/{user_id}")
def read_user(user_id: str, subject: str = Depends(get_current_subject)):
# Ensure the subject is allowed to view this user document
if not ObjectId.is_valid(user_id):
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Invalid user ID")
# Critical: include subject in query scope
document = users.find_one({"_id": ObjectId(user_id), "owner_id": subject})
if document is None:
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Access denied")
# Remove sensitive fields before returning
document.pop("password_hash", None)
return {"user_id": str(document["_id"]), "username": document.get("username")}
# Multi-tenant example with tenant_id field
def get_current_tenant() -> str:
# Extract tenant from subdomain, header, or token claims
return "tenant-uuid"
@app.get("/records/{record_id}")
def read_record(record_id: str, tenant_id: str = Depends(get_current_tenant), subject: str = Depends(get_current_subject)):
if not ObjectId.is_valid(record_id):
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Invalid record ID")
document = db["records"].find_one({
"_id": ObjectId(record_id),
"tenant_id": tenant_id,
"allowed_subjects": subject # assuming an array field that enumerates allowed subjects
})
if document is None:
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Record not found or insufficient permissions")
return {"record_id": str(document["_id"]), "data": document.get("payload")}
Key remediation points: always include the subject or tenant identifier in the MongoDB filter, validate and sanitize input identifiers (e.g., check ObjectId.is_valid), and return generic error messages to avoid information leakage. Avoid exposing internal _id values directly in URLs when possible; consider mapping to opaque references if necessary. middleBrick’s scans can verify that such scoping logic exists in runtime responses and flag endpoints where identifiers are used without authorization checks.
Additionally, apply principle of least privilege to the MongoDB connection used by Fastapi. Ensure the database user has only the permissions required for the operation (e.g., find on specific collections) and avoid broad read access at the database level. Regularly rotate credentials and use network-level restrictions to reduce the impact of potential misconfigurations.
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 |