Path Traversal in Express with Firestore
Path Traversal in Express with Firestore — how this specific combination creates or exposes the vulnerability
Path Traversal occurs when an attacker manipulates a file or document path to access resources outside the intended directory or collection. In an Express application that uses Cloud Firestore, this typically arises when user-supplied input such as an ID, filename, or document path is concatenated into Firestore references or file-system paths without proper validation or sanitization. Because Firestore rules do not automatically protect against maliciously crafted document paths, an endpoint that builds a document reference from unchecked input can expose unintended documents.
Consider an Express route that retrieves a user profile using a document ID provided via a query parameter:
// Risky: unsanitized user input used to reference a Firestore document
app.get('/profile', async (req, res) => {
const { userId } = req.query; // e.g., userId=../../../secrets/001
const docRef = db.collection('profiles').doc(userId);
const snap = await docRef.get();
res.json(snap.data() || {});
});
If the application naïvely uses the raw userId to form a document path, an attacker can attempt traversals like ../../../secrets/001. While Firestore path normalization usually prevents directory traversal outside a collection’s document hierarchy, improper validation can still lead to accessing or inferring the existence of other documents, especially when combined with server-side logic that interprets path segments differently. Moreover, if the application later uses the resolved path for file-system operations—such as serving user-uploaded assets—Firestore document names might inadvertently map to local file paths, creating a hybrid traversal vector.
In the context of middleBrick’s 12 security checks, Path Traversal related findings may surface under Property Authorization, Input Validation, and Unsafe Consumption. For example, an unauthenticated LLM endpoint that exposes Firestore-backed data could be probed via active prompt injection techniques to test whether document paths can be coerced. middleBrick’s scan would report a high-severity finding with guidance to treat user input as untrusted and to enforce strict allowlists on path characters.
Additionally, Firestore’s REST and SDKs expect document paths that adhere to strict rules: they must not contain forward slashes as part of a document ID unless those slashes are part of a subcollection structure that the application explicitly intends. An Express route that builds paths by concatenating user input increases the risk of either malformed references or unintended document access, which may be detectable via response differences or timing. Therefore, validating and sanitizing input against an allowlist of safe characters is essential before using it in a Firestore document reference.
Firestore-Specific Remediation in Express — concrete code fixes
To mitigate Path Traversal in Express applications using Firestore, enforce strict input validation, avoid direct concatenation of user input into document paths, and use Firestore’s built-in APIs safely. Below are concrete, secure implementations.
1. Validate and sanitize document IDs
Use an allowlist approach to ensure document IDs contain only safe characters. Reject inputs that include path separators, control characters, or unexpected patterns.
const isValidDocumentId = (id) => {
// Allow alphanumeric, hyphen, underscore, and dot; length 1–1500
return /^[a-zA-Z0-9_\-\.]{1,1500}$/.test(id);
};
app.get('/profile', async (req, res) => {
const { userId } = req.query;
if (!userId || !isValidDocumentId(userId)) {
return res.status(400).json({ error: 'Invalid user ID' });
}
const docRef = db.collection('profiles').doc(userId);
const snap = await docRef.get();
res.json(snap.data() || {});
});
2. Use Firestore document IDs safely with collections
Always reference documents via collection and document ID pairs rather than building raw paths. If you need subcollections, explicitly define the structure instead of inferring it from user input.
app.get('/user-post', async (req, res) => {
const { uid, postId } = req.query;
if (!uid || !isValidDocumentId(uid) || !isValidDocumentId(postId)) {
return res.status(400).json({ error: 'Invalid parameters' });
}
const postRef = db
.collection('users')
.doc(uid)
.collection('posts')
.doc(postId);
const snap = await postRef.get();
res.json(snap.data() || {});
});
3. Avoid using Firestore document names as filesystem paths
If you must serve files based on Firestore metadata, map document IDs to filesystem paths through a controlled lookup table rather than direct string conversion.
const path = require('path');
app.get('/avatar/:id', async (req, res) => {
const { id } = req.params;
if (!isValidDocumentId(id)) {
return res.status(400).send('Invalid ID');
}
const metaDoc = await db.collection('avatars').doc(id).get();
if (!metaDoc.exists) {
return res.status(404).send('Not found');
}
const filename = metaDoc.data().filename; // stored filename, not user input
const filePath = path.join('/safe/avatars', filename);
res.sendFile(filePath);
});
These patterns reduce the attack surface by ensuring that user input never directly dictates document or file paths. middleBrick’s dashboard and CLI can be used to verify that your endpoints resist path traversal attempts by running scans that include input validation and property authorization checks.
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 |