Insecure Direct Object Reference in Fastapi with Firestore
Insecure Direct Object Reference in Fastapi with Firestore
Insecure Direct Object Reference (IDOR) occurs when an API exposes a direct reference to an internal object—such as a Firestore document ID—and relies solely on the client not to tamper with it. In a Fastapi service that uses Google Cloud Firestore as the backend, this typically manifests when an endpoint uses a user-supplied identifier (e.g., a document ID or a numeric key) to retrieve or modify a resource without verifying that the requesting user has permission for that specific object.
Consider a Fastapi route that accepts a document_id path parameter to fetch a user’s profile stored in Firestore. If the route directly uses this ID in a get call without confirming that the profile belongs to the authenticated caller, an attacker can enumerate sequential or predictable IDs and access or modify other users’ data. This is an IDOR, and when the backend uses Firestore rules only for server-side enforcement without corresponding application-level authorization, the vulnerability becomes reachable.
Firestore itself does not introduce IDOR; rather, the risk arises from how the Fastapi service constructs and uses references to Firestore documents. For example, a route like /users/{document_id} that calls db.collection("users").document(document_id).get() without validating ownership or tenant context allows horizontal IDOR. Attack patterns include changing the ID to another user’s document ID, manipulating array indices, or exploiting exported references that point to sensitive collections.
Additionally, Firestore’s security rules can inadvertently support IDOR if they rely only on request.auth.uid being present, without scoping reads/writes to the specific document the user is allowed to access. In such configurations, a Fastapi endpoint that assumes rules will block unauthorized access may still expose an IDOR if the app does not enforce authorization before issuing the database call.
To illustrate, a vulnerable Fastapi route might look like this, where the caller provides a user_ref that maps to a Firestore document path:
from fastapi import FastAPI, Depends, HTTPException from google.cloud import firestore app = FastAPI() db = firestore.Client() @app.get("/users/{user_ref}") def get_user(user_ref: str): doc = db.collection("users").document(user_ref).get() if not doc.exists: raise HTTPException(status_code=404, detail="Not found") return doc.to_dict()An attacker who iterates through numeric or UUID-like references can enumerate profiles, demonstrating a classic IDOR. Even if Firestore rules restrict reads to the authenticated user, the Fastapi service must still validate that the requested document ID matches the user’s allowed set, because rules are not a substitute for application-level checks in this context.
Another scenario involves indirect references, such as an exported document name returned by one endpoint being reused by another without validation. For instance, a list endpoint might return full document paths that a downstream route uses directly. If those paths are not scoped to the requester’s permissions, IDOR persists despite Firestore rules.
Effective mitigation in Fastapi with Firestore requires coupling authentication with explicit authorization checks before any Firestore operation. This includes verifying that the authenticated user owns or is permitted to act on the referenced object, using tenant identifiers, scopes, or role checks, and avoiding direct exposure of internal document IDs when possible.
Firestore-Specific Remediation in Fastapi — concrete code fixes
Remediation for IDOR in Fastapi with Firestore centers on ensuring that every document reference is validated against the requester’s permissions. Below are concrete, idiomatic code examples that demonstrate secure patterns.
1. Use application-level ownership checks. Instead of trusting the client-supplied document ID, map it to the authenticated user’s known identifiers. For example, if users are stored under a collection keyed by their UID, derive the document ID from the authenticated subject rather than accepting it from the client:
from fastapi import FastAPI, Depends, HTTPException from google.cloud import firestore from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials app = FastAPI() db = firestore.Client() security = HTTPBearer() def get_current_user(credentials: HTTPAuthorizationCredentials = Depends(security)): # Validate token and extract user identity (e.g., UID) # This is a placeholder for your auth logic return {"uid": "user_123"} @app.get("/users/me") def get_current_user_profile(user: dict = Depends(get_current_user)): doc = db.collection("users").document(user["uid"]).get() if not doc.exists: raise HTTPException(status_code=404, detail="Not found") return doc.to_dict()2. When you must accept a document ID, enforce tenant or ownership scoping. Retrieve the document and confirm that it belongs to the caller, for example by checking a
user_idfield inside the document:@app.get("/profiles/{profile_id}") def get_profile(profile_id: str, user: dict = Depends(get_current_user)): doc = db.collection("profiles").document(profile_id).get() if not doc.exists: raise HTTPException(status_code=404, detail="Not found") data = doc.to_dict() if data.get("user_id") != user["uid"]: raise HTTPException(status_code=403, detail="Forbidden") return data3. For Firestore collections where documents are shared within groups, validate group membership before allowing access. Fetch the group document and confirm the user is listed:
@app.get("/groups/{group_id}/notes/{note_id}") def get_group_note(group_id: str, note_id: str, user: dict = Depends(get_current_user)): group_ref = db.collection("groups").document(group_id) group_doc = group_ref.get() if not group_doc.exists: raise HTTPException(status_code=404, detail="Group not found") if user["uid"] not in group_doc.to_dict().get("members", []): raise HTTPException(status_code=403, detail="Not a member") note = group_ref.collection("notes").document(note_id).get() if not note.exists: raise HTTPException(status_code=404, detail="Note not found") return note.to_dict()4. Avoid returning raw document references or paths to the client. If you need to provide links, use short-lived, signed tokens or opaque identifiers that map to document IDs server-side, preventing enumeration.
These patterns ensure that Firestore document references are never used in isolation and are always cross-checked against the authenticated user’s context, effectively mitigating IDOR in Fastapi applications backed by Firestore.
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 |