Path Traversal in Firestore
How Path Traversal Manifests in Firestore
Path traversal vulnerabilities in Firestore occur when applications construct document or collection paths using untrusted user input without proper validation. Unlike traditional file system path traversal, Firestore path traversal allows attackers to access, modify, or delete documents they shouldn't have access to by manipulating path components.
The most common Firestore path traversal pattern involves document ID manipulation. Consider this vulnerable code:
const userId = req.query.userId; // User-controlled input
const docRef = db.collection('users').doc(userId);
const userDoc = await docRef.get();
An attacker could supply ../admin/settings or ./../admin/settings as the userId, potentially accessing administrative documents if path traversal isn't properly handled.
Firestore path traversal also occurs through collection path manipulation. When applications dynamically build collection paths based on user input:
const orgId = req.query.orgId;
const collectionPath = `organizations/${orgId}/projects/active`;
const projectsRef = db.collection(collectionPath);
An attacker could manipulate orgId to traverse into other organizations' data structures, accessing organizations/12345/projects/active when they should only access their own organization.
Another variant involves wildcard expansion abuse. Firestore doesn't support traditional .. path traversal, but attackers can exploit wildcard patterns:
// Vulnerable: allows wildcard expansion
const ref = db.collection(`users/${userId}/projects/*`);
This could return all projects across all users if userId is manipulated correctly, especially when combined with improper security rules.
Document ID encoding issues create additional traversal vectors. Firestore document IDs can contain special characters, and improper encoding/decoding can lead to path manipulation:
// Vulnerable: doesn't validate document ID format
const docId = decodeURIComponent(req.query.docId);
const docRef = db.collection('shared').doc(docId);
An attacker could URL-encode path traversal characters to bypass simple validation checks.
Batch operations amplify traversal risks. When applications process arrays of document paths:
const docPaths = req.body.docPaths; // Array of paths from user
const batch = db.batch();
for (const path of docPaths) {
const docRef = db.doc(path);
batch.delete(docRef);
}
An attacker could supply paths like users/ADMIN/settings to delete administrative documents if the application doesn't validate each path individually.
Firestore's hierarchical data model makes traversal particularly dangerous because it's not just about accessing files—it's about accessing entire subtrees of data. A single traversal vulnerability could expose or compromise an entire organizational structure stored in Firestore.
Firestore-Specific Detection
Detecting Firestore path traversal requires both static code analysis and dynamic runtime scanning. middleBrick's Firestore-specific scanning examines your API endpoints for traversal vulnerabilities by testing various path manipulation patterns.
Static detection focuses on code patterns that construct Firestore paths from user input. middleBrick analyzes your application code for:
const ref = db.collection(
req.query.path
);
The scanner flags any dynamic path construction where user input isn't properly validated or sanitized. It specifically looks for:
- Direct interpolation of request parameters into collection/document paths
- Missing validation on document IDs and collection names
- Wildcard usage with user-controlled variables
- Array processing of document paths without individual validation
- Encoding/decoding operations on path components
Dynamic scanning tests your Firestore API endpoints with traversal payloads. middleBrick sends requests with specially crafted path components:
GET /api/users?userId=../admin/settings HTTP/1.1
Host: yourapi.com
The scanner observes how your application handles these requests, checking for:
- Whether traversal attempts succeed in accessing unauthorized documents
- If error messages reveal sensitive path information
- Whether wildcard patterns return broader results than expected
- If batch operations process malicious paths
middleBrick's LLM security features are particularly relevant for Firestore path traversal in AI applications. If your Firestore data feeds into LLM prompts, path traversal could expose system prompts or training data:
// Vulnerable: path traversal could expose system prompts
const systemPrompt = await db.doc(`prompts/${userId}/system`).get();
The scanner tests for 27 regex patterns that detect system prompt leakage and active prompt injection attempts that could exploit traversal vulnerabilities.
Security rule analysis complements path traversal detection. middleBrick examines your Firestore security rules to ensure they properly restrict access based on authenticated user identity rather than relying solely on path structure:
// Vulnerable rule: trusts path structure
allow read, write: if request.auth.uid == docId;
The scanner recommends rules that validate user identity independently of path traversal attempts.
Firestore-Specific Remediation
Remediating Firestore path traversal requires a defense-in-depth approach combining input validation, proper security rules, and safe path construction patterns.
Input validation is the first line of defense. Always validate document IDs and collection names against strict patterns:
function validateDocumentId(docId) {
// Only allow alphanumeric, underscores, and hyphens
const validPattern = /^[a-zA-Z0-9_-]+$/;
if (!validPattern.test(docId)) {
throw new Error('Invalid document ID format');
}
// Reject any ID that looks like a path
if (docId.includes('/') || docId.includes('\')) {
throw new Error('Document IDs cannot contain path separators');
}
return docId;
}
// Usage
const userId = validateDocumentId(req.query.userId);
const docRef = db.collection('users').doc(userId);
Whitelist allowed characters rather than blacklisting dangerous ones. This prevents attackers from using encoded characters or Unicode variations.
Security rules must validate user identity independently of path structure. Never rely on path components for authorization:
service cloud.firestore {
match /databases/{database}/documents {
// Secure: validates user ID from auth, not from path
match /users/{userId}/projects/{projectId} {
allow read, write: if
request.auth != null &&
request.auth.uid == userId &&
exists(/databases/$(database)/documents/users/$(userId));
}
// Even more secure: validate user exists and has access
match /organizations/{orgId}/projects/{projectId} {
allow read, write: if
request.auth != null &&
exists(
/databases/$(database)/documents/
organizations/$(orgId)/members/$(request.auth.uid)
);
}
}
}
These rules verify the authenticated user actually exists in the requested path rather than trusting the path structure itself.
Safe path construction patterns prevent traversal at the API level. Use factory functions that validate and sanitize paths:
class FirestorePathBuilder {
constructor(db) {
this.db = db;
}
buildUserDocument(userId) {
const validatedId = validateDocumentId(userId);
return this.db.collection('users').doc(validatedId);
}
buildOrganizationProject(orgId, projectId) {
const validatedOrg = validateDocumentId(orgId);
const validatedProject = validateDocumentId(projectId);
return this.db
.collection('organizations')
.doc(validatedOrg)
.collection('projects')
.doc(validatedProject);
}
validateCollectionPath(collectionPath) {
// Ensure collection path doesn't contain traversal patterns
const segments = collectionPath.split('/');
if (segments.length % 2 !== 1) {
throw new Error('Invalid collection path');
}
return collectionPath;
}
}
// Usage
const pathBuilder = new FirestorePathBuilder(db);
const userDoc = pathBuilder.buildUserDocument(req.query.userId);
This approach centralizes validation and prevents traversal patterns from being constructed.
Batch operation security requires individual path validation. Never process arrays of paths without validation:
async function safeBatchDelete(db, docPaths, userId) {
const batch = db.batch();
const validatedPaths = [];
for (const path of docPaths) {
const docRef = db.doc(path);
// Validate the path belongs to the user
const segments = docRef.path.split('/');
if (segments[1] !== userId) {
throw new Error('Path traversal attempt detected');
}
validatedPaths.push(docRef);
}
for (const docRef of validatedPaths) {
batch.delete(docRef);
}
return batch.commit();
}
This ensures each path in batch operations is individually validated before processing.
For LLM applications using Firestore, implement strict data isolation. Never store system prompts or sensitive training data in user-accessible paths:
// Secure: separate storage for system prompts
const systemPromptsRef = db.collection('system_prompts').doc('default');
const userPromptsRef = db.collection('users').doc(userId).collection('prompts');
// Never allow user-controlled paths to access system prompts
middleBrick's continuous monitoring can help maintain these security measures by regularly scanning your APIs for new traversal vulnerabilities as your codebase evolves.
Related CWEs: inputValidation
| CWE ID | Name | Severity |
|---|---|---|
| CWE-20 | Improper Input Validation | HIGH |
| CWE-22 | Path Traversal | HIGH |
| CWE-74 | Injection | CRITICAL |
| CWE-77 | Command Injection | CRITICAL |
| CWE-78 | OS Command Injection | CRITICAL |
| CWE-79 | Cross-site Scripting (XSS) | HIGH |
| CWE-89 | SQL Injection | CRITICAL |
| CWE-90 | LDAP Injection | HIGH |
| CWE-91 | XML Injection | HIGH |
| CWE-94 | Code Injection | CRITICAL |