HIGH nosql injectionfastapifirestore

Nosql Injection in Fastapi with Firestore

Nosql Injection in Fastapi with Firestore — how this specific combination creates or exposes the vulnerability

NoSQL injection in a FastAPI application that uses Google Cloud Firestore occurs when user-controlled input is directly incorporated into queries or document lookups without proper validation or parameterization. Firestore supports structured queries, map/array lookups, and raw field comparisons, and these features can be abused if input is concatenated into query filters or document paths. A FastAPI endpoint that builds a query from request parameters can unintentionally allow an attacker to change query logic, read documents they should not access, or cause excessive reads that affect cost or availability.

Consider a typical pattern where a client supplies a document ID or a field value to retrieve a resource. If the server directly uses that value in a collection() or in a where() clause without normalization and strict allowlisting, an attacker can inject Firestore syntax that changes query semantics. For example, an input like {'$gt': ''} or array operators such as array-contains-any can expand the result set or bypass intended filters. Because FastAPI often deserializes JSON payloads into Python dicts before they reach application logic, nested operators can be passed through if the API does not explicitly sanitize or reshape the data.

Another common scenario is using user input to construct document paths for get or set operations. If an endpoint builds a path like f'users/{user_id}/settings/{setting_name}' using values from the request without strict validation, an attacker might supply path segments that traverse to unrelated collections or escalate access to administrative documents. Firestore security rules can mitigate some risks, but they are not a substitute for secure server-side handling. Incorrect rule configurations or overly permissive rules combined with dynamic path building can expose sensitive data or enable unauthorized writes.

The combination of FastAPI’s automatic request parsing and Firestore’s flexible query syntax means that developers must explicitly validate and parameterize all inputs. Relying on client-side constraints or assuming that Firestore will reject invalid queries is insufficient because the server is responsible for constructing safe operations. Without structured input validation, allowlists for field names, and disciplined use of Firestore’s API methods, NoSQL injection becomes a realistic threat vector in this stack.

Firestore-Specific Remediation in Fastapi — concrete code fixes

Remediation focuses on strict input validation, using Firestore’s parameter-safe methods, and avoiding string-based or dynamic query construction. Always treat user input as untrusted and reshape it to match expected schemas before using it in database operations.

Below are concrete, working FastAPI examples that demonstrate secure handling with Firestore.

from fastapi import FastAPI, HTTPException, Query
from google.cloud import firestore
import re

app = FastAPI()
db = firestore.Client()

# Safe document retrieval by ID with strict allowlist and format check
@app.get("/users/{user_id}")
def get_user(user_id: str = Query(..., regex=r'^[a-zA-Z0-9_-]{1,64}$')):
    # Enforce document ID rules to prevent path traversal or injection via ID
    if not re.match(r'^[a-zA-Z0-9_-]{1,64}$', user_id):
        raise HTTPException(status_code=400, detail="Invalid user ID")
    doc_ref = db.collection('users').document(user_id)
    doc = doc_ref.get()
    if not doc.exists:
        raise HTTPException(status_code=404, detail="User not found")
    return {doc.id: doc.to_dict()}

# Safe query using explicit field allowlist and parameterization
@app.get("/items/")
def list_items(category: str = Query(..., min_length=1, max_length=50)):
    ALLOWED_CATEGORIES = {"books", "electronics", "clothing"}
    if category not in ALLOWED_CATEGORIES:
        raise HTTPException(status_code=400, detail="Invalid category")
    # Parameterized query using Firestore's built-in parameter support
    collection_ref = db.collection('items')
    query = collection_ref.where('category', '==', category).limit(20)
    results = query.stream()
    return [{'id': doc.id, **doc.to_dict()} for doc in results]

# Safe update with mapped fields and no dynamic operator injection
@app.patch("/settings/{doc_id}")
def update_settings(doc_id: str, updates: dict):
    # Allow only known, safe field names; reject any keys that look like operators
    ALLOWED_FIELDS = {'theme', 'language', 'notifications_enabled'}
    invalid_keys = [k for k in updates.keys() if k not in ALLOWED_FIELDS]
    if invalid_keys:
        raise HTTPException(status_code=400, detail=f"Invalid fields: {invalid_keys}")
    # Reject any values that contain Firestore-like operator patterns
    for v in updates.values():
        if isinstance(v, str) and re.search(r'^[\$\{\}\[\].]', v):
            raise HTTPException(status_code=400, detail="Invalid value format")
    doc_ref = db.collection('settings').document(doc_id)
    doc_ref.update(updates)
    return {'status': 'updated'}

# Example of using array-contains with strict allowlist values
@app.post("/tags/")
def add_tags(doc_id: str = Query(..., min_length=1), tags: list[str] = Query(...)):
    ALLOWED_TAGS = {"work", "personal", "urgent", "review"}
    normalized = [t for t in tags if t in ALLOWED_TAGS]
    if not normalized:
        raise HTTPException(status_code=400, detail="No valid tags provided")
    doc_ref = db.collection('docs').document(doc_id)
    doc_ref.update({'tags': firestore.ArrayUnion(normalized)})
    return {'updated_tags': normalized}

Key remediation practices:

  • Use strict allowlists for categories, field names, and document IDs; reject anything that does not explicitly match expected patterns.
  • Parameterize queries with Firestore’s where(field, op, value) pattern instead of building query strings or injecting user input into operator positions.
  • Validate and sanitize array inputs with allowlists and avoid passing raw user input into operators such as array-contains-any unless values are strictly constrained.
  • Reject keys that start with special characters (e.g., $, {) to prevent operator injection through dictionary keys.
  • Apply server-side schema checks and use Firestore security rules as a defense-in-depth layer, but do not rely on them alone to prevent injection on the server.

Frequently Asked Questions

How can I validate Firestore document IDs safely in FastAPI?
Use a strict regex allowlist (e.g., ^[a-zA-Z0-9_-]{1,64}$) in path parameters and reject any input containing dots, slashes, or reserved characters before constructing document references.
Is it safe to use user input in Firestore where clauses in FastAPI?
Yes, if you parameterize the queries with Firestore’s built-in methods and validate/sanitize inputs against an allowlist; never concatenate user input into field names or operator values.