Graphql Introspection in Fastapi with Firestore
Graphql Introspection in Fastapi with Firestore — how this specific combination creates or exposes the vulnerability
GraphQL introspection allows clients to query the schema for types, queries, and mutations. In a FastAPI application that serves a GraphQL endpoint backed by Firestore, enabling introspection without restrictions exposes implementation details, query patterns, and data structure. This can reveal sensitive field names, document paths, and relationships that aid reconnaissance for attackers.
When GraphQL introspection is left available in production, an attacker can programmatically retrieve the full schema, including custom scalars, directives, and Firestore document models. This information can be combined with known Firestore security rules weaknesses or misconfigured indexes to probe for data access paths. Because FastAPI typically serves GraphQL through libraries such as strawberry or graphene, the introspection query is often accessible at the same route used for normal operations, requiring no authentication by default.
Insecure default configurations are a common pitfall. For example, if the GraphQL handler does not explicitly disable introspection or apply role-based filtering, any unauthenticated request can execute an introspection query. Attackers can then map fields that reference Firestore document IDs, timestamps, or user-specific collections, which may lead to further attacks such as BOLA/IDOR if authorization checks are inconsistent between the GraphQL resolver and Firestore rules.
Consider a FastAPI route that forwards GraphQL queries to a resolver chain interacting with the Firestore Python SDK. If introspection is enabled, an attacker can discover query names like getUserDocuments or fields such as firestorePath, revealing how data is partitioned. This becomes especially risky when combined with overly permissive Firestore security rules that do not enforce ownership at the document level.
To mitigate this risk, disable introspection in production or gate it behind authentication and strict authorization. Combine this with well-defined Firestore security rules that validate user identity and scope data access. Tools like middleBrick can detect whether introspection is exposed and highlight related configuration issues, providing prioritized findings with severity levels and remediation guidance.
Firestore-Specific Remediation in Fastapi — concrete code fixes
Remediation focuses on controlling introspection and ensuring that data access patterns do not leak through GraphQL resolvers. Below are concrete FastAPI examples that demonstrate secure handling when integrating with Firestore.
Disable introspection explicitly in production
If using strawberry, disable introspection by setting introspection=False in the schema configuration:
import strawberry
from typing import List
@strawberry.type
class DocumentNode:
id: str
name: str
owner_id: str
@strawberry.type
class Query:
@strawberry.field
def list_documents(self) -> List[DocumentNode]:
# Resolver implementation omitted for brevity
pass
schema = strawberry.Schema(
query=Query,
introspection=False # Disable introspection in production
)
Secure Firestore access with explicit ownership checks
Ensure that each Firestore read is scoped to the requesting user. Use the Firebase Admin SDK to validate ownership server-side:
from fastapi import Depends, HTTPException, status
import firebase_admin
from firebase_admin import credentials, firestore
from typing import List
cred = credentials.ApplicationDefault()
firebase_admin.initialize_app(cred)
db = firestore.client()
def get_current_user_id() -> str:
# Assume a dependency that extracts user identity securely
return "user-uuid-123"
def fetch_user_documents(user_id: str) -> List[dict]:
docs = (db.collection("users")
.document(user_id)
.collection("documents")
.stream())
return [doc.to_dict() | {"id": doc.id} for doc in docs]
@strawberry.type
class Query:
@strawberry.field
def my_documents(self, user_id: str = Depends(get_current_user_id)) -> List[dict]:
return fetch_user_documents(user_id)
Apply rule-based authorization at the resolver level
Do not rely solely on Firestore security rules for GraphQL operations. Enforce additional checks in resolvers to ensure that the requesting user is authorized for the specific document:
from fastapi import HTTPException
def fetch_document_with_check(document_id: str, user_id: str) -> dict:
doc_ref = db.collection("documents").document(document_id)
doc = doc_ref.get()
if not doc.exists:
raise HTTPException(status_code=404, detail="Document not found")
data = doc.to_dict()
if data.get("owner_id") != user_id:
raise HTTPException(status_code=403, detail="Forbidden: insufficient permissions")
return {"id": doc.id, **data}
Environment-aware configuration
Use environment variables to toggle introspection and logging verbosity. In production, ensure that introspection is disabled and sensitive debug information is not surfaced through GraphQL errors:
import os
from fastapi import FastAPI
app = FastAPI()
INTROSPECTION_ENABLED = os.getenv("GRAPHQL_INTROSPECTION", "false").lower() == "true"
@app.get("/graphql")
async def graphql_endpoint(query: str):
# Pass configuration to your GraphQL handler
pass
Validate and sanitize inputs to prevent injection
Even with introspection disabled, always validate inputs to mitigate injection risks. Use parameterized queries and avoid string concatenation when building Firestore paths:
from google.cloud import firestore
def safe_get_document(collection_name: str, document_id: str) -> dict:
# Validate collection name against an allowlist
allowed_collections = {"documents", "profiles", "settings"}
if collection_name not in allowed_collections:
raise ValueError("Invalid collection")
doc_ref = db.collection(collection_name).document(document_id)
return doc_ref.get().to_dict()
By combining schema-level controls, server-side ownership validation, and strict input validation, you reduce the attack surface introduced by GraphQL introspection when interfacing with Firestore in FastAPI.
Related CWEs: dataExposure
| CWE ID | Name | Severity |
|---|---|---|
| CWE-200 | Exposure of Sensitive Information | HIGH |
| CWE-209 | Error Information Disclosure | MEDIUM |
| CWE-213 | Exposure of Sensitive Information Due to Incompatible Policies | HIGH |
| CWE-215 | Insertion of Sensitive Information Into Debugging Code | MEDIUM |
| CWE-312 | Cleartext Storage of Sensitive Information | HIGH |
| CWE-359 | Exposure of Private Personal Information (PII) | HIGH |
| CWE-522 | Insufficiently Protected Credentials | CRITICAL |
| CWE-532 | Insertion of Sensitive Information into Log File | MEDIUM |
| CWE-538 | Insertion of Sensitive Information into Externally-Accessible File | HIGH |
| CWE-540 | Inclusion of Sensitive Information in Source Code | HIGH |