Cross Site Request Forgery in Fastapi with Firestore
Cross Site Request Forgery in Fastapi with Firestore — how this specific combination creates or exposes the vulnerability
Cross Site Request Forgery (CSRF) in a FastAPI application that uses Google Cloud Firestore arises when state-changing operations rely on cookies or session tokens for authentication while lacking anti-CSRF protections. FastAPI does not enforce CSRF mitigation by default, and if endpoints assume requests originate only from your frontend (e.g., browser JavaScript), forged requests from attacker-controlled sites can succeed.
When Firestore is used as the backend datastore, the risk centers on how authentication and authorization are enforced. If your FastAPI service uses session cookies or an identity provider that issues cookies, and those cookies are automatically included by browsers on cross-origin requests, an attacker can craft forms or scripts on their own site that invoke your FastAPI endpoints. Because Firestore security rules typically enforce identity at the database level (e.g., via Firebase Authentication UID rules), the authorization decision may appear correct in logs, but the request itself was initiated without user intent.
Consider a FastAPI endpoint that updates a Firestore document representing a user profile. If the endpoint reads the UID from an authenticated session cookie and directly applies the update to Firestore based on request JSON, a forged request from another site can perform unwanted changes. For example, an attacker can trick a victim into submitting a form that calls /api/profile with modified data. Firestore rules may allow the write because the UID matches, but the operation was not intentionally initiated by the user. The unauthenticated attack surface is expanded if your API also exposes endpoints that rely on CORS configurations that are too permissive or if preflight checks are not strict, enabling cross-origin credentialed requests.
Additionally, if your FastAPI routes serve both API and static assets, or if cookie attributes like SameSite are not set strictly, browsers may include authentication cookies in cross-site requests. Firestore’s UID-based rules do not protect against CSRF at the API layer; they only enforce data-level permissions once the request reaches Firestore. Therefore, CSRF in this stack manifests as a lack of origin validation and missing synchronization tokens (e.g., anti-CSRF tokens or SameSite cookies) around mutating endpoints, even when Firestore rules are correctly scoped.
Firestore-Specific Remediation in Fastapi — concrete code fixes
To mitigate CSRF for FastAPI endpoints that interact with Firestore, enforce strict origin and credential handling, and use anti-CSRF tokens for state-changing operations. The following patterns assume you use Firebase Admin SDK or a Firestore client library to access Firestore securely within FastAPI routes.
1. Set strict CORS and cookie attributes
Ensure CORS middleware only allows trusted origins and does not expose credentials to untrusted domains. Configure cookies with SameSite=Lax (or Strict for sensitive actions) and Secure.
from fastapi import FastAPI, Response
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=["https://your-trusted-frontend.com"],
allow_credentials=True,
allow_methods=["GET", "POST"],
allow_headers=["Authorization", "Content-Type"],
)
@app.post("/api/profile")
async def update_profile(response: Response):
response.set_cookie(
key="session_id",
value="...",
httponly=True,
secure=True,
samesite="Lax",
)
return {"status": "ok"}
2. Use anti-CSRF tokens for mutating endpoints
Generate and validate per-request or per-session CSRF tokens for POST, PUT, PATCH, and DELETE routes. Store the token server-side (e.g., in session data) and require it in a header or form field. Below is an example using a simple token stored in a secure cookie and validated on each mutation.
import secrets
from fastapi import FastAPI, Request, Cookie, HTTPException
from fastapi.responses import JSONResponse
app = FastAPI()
def generate_csrf_token() -> str:
return secrets.token_urlsafe(32)
@app.post("/api/firestore/update")
async def update_firestore(
request: Request,
csrf_token: str = Cookie(None),
x_csrf_token: str | None = Header(None),
):
if not csrf_token or csrf_token != x_csrf_token:
raise HTTPException(status_code=403, detail="Invalid or missing CSRF token")
# Proceed to interact with Firestore safely
# Example Firestore write using the Firebase Admin SDK client initialized elsewhere
# from firebase_admin import firestore
# db = firestore.client()
# db.collection("items").document(doc_id).update({...})
return JSONResponse(content={"message": "updated"})
3. Validate Origin and Referer headers cautiously
For endpoints that do not rely on cookies for authentication (e.g., token-based), you can additionally validate the Origin or Referer header as a secondary defense. However, do not rely on this alone, as these headers can be omitted in some scenarios.
from fastapi import FastAPI, Request, HTTPException
app = FastAPI()
@app.post("/api/order/create")
async def create_order(request: Request):
origin = request.headers.get("origin")
referer = request.headers.get("referer")
allowed = {"https://your-trusted-frontend.com"}
if origin not in allowed or referer not in allowed:
raise HTTPException(status_code=403, detail="Invalid request origin")
# Continue with Firestore transaction
return {"order_id": "generated"}
4. Follow principle of least privilege in Firestore rules
Ensure Firestore security rules are scoped to the minimal required resources and UID. While this does not prevent CSRF at the API layer, it limits the impact of a successful forged request.
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId} {
allow read, write: if request.auth != null && request.auth.uid == userId;
}
}
}