HIGH insecure direct object referencefastapifirestore

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_id field 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 data

3. 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 IDNameSeverity
CWE-250Execution with Unnecessary Privileges HIGH
CWE-639Insecure Direct Object Reference CRITICAL
CWE-732Incorrect Permission Assignment HIGH

Frequently Asked Questions

Does using Firestore security rules alone prevent IDOR in Fastapi?
No. Firestore rules provide a server-side safety net but should not replace application-level authorization in Fastapi. Rules may not cover all contextual constraints (e.g., tenant isolation), so always validate document ownership in your API layer before performing reads or writes.
How can I prevent IDOR when I need to expose document IDs to the client?
Avoid exposing raw Firestore document IDs. Use indirect references, short-lived signed tokens, or opaque identifiers mapped server-side to document IDs. Always re-check permissions on each request, confirming that the authenticated subject is authorized for the target document.