Dictionary Attack in Firestore
How Dictionary Attack Manifests in Firestore
In Firestore, a dictionary attack (often categorized under BOLA/IDOR) exploits predictable document identifiers or query parameters to access unauthorized data. Firestore's NoSQL structure uses path-based document references (e.g., /users/{userId}/orders/{orderId}). When application logic uses sequential numbers, timestamps, email addresses, or other guessable values as document IDs, an attacker can systematically enumerate these IDs via unauthenticated or low-privilege API endpoints.
Common Firestore-specific attack patterns include:
- Predictable Document ID Enumeration: If an API endpoint like
GET /api/orders/{orderId}directly maps to a Firestore document path without proper authorization checks in Firestore Security Rules, an attacker can iterate through numeric IDs (1,2,3) or timestamp-based IDs to retrieve other users' orders. - Collection Query Parameter Manipulation: APIs that accept query parameters (e.g.,
GET /api/[email protected]) and use them in Firestore queries (.where('email', '==', req.query.email)) may expose data if the query lacks user-contextual filters. An attacker can brute-force email addresses or other fields. - Subcollection Path Traversal: Improperly designed routes might allow path manipulation (e.g.,
/api/data/../otherCollection/doc) if the backend concatenates user input into Firestore paths without sanitization, though Firestore's SDK typically normalizes paths. - Misconfigured Security Rules: Firestore Security Rules that use
request.queryorresource.datawithout validating againstrequest.auth.uidcan leak data. For example, a rule likeallow read: if true;on a user's private collection is trivially exploitable.
These attacks are particularly severe because Firestore often stores sensitive user data (PII, financial records) in nested documents. Unlike SQL injection, dictionary attacks abuse business logic flaws and weak access controls in the document data model.
Firestore-Specific Detection
Detecting dictionary attack vulnerabilities in Firestore-backed APIs involves testing for unauthorized data access through parameter manipulation. middleBrick's BOLA/IDOR check specifically probes these issues by:
- Automatically identifying parameterized paths in the API (e.g.,
/orders/{id},/users?email=) and attempting sequential or dictionary-based ID guessing. - Analyzing Firestore Security Rules (if an OpenAPI spec with Firestore references is provided) to detect missing
request.authchecks or over-permissiveallow readstatements. - Cross-referencing runtime responses: if changing an ID parameter returns valid data (200 OK) with different content, it indicates a potential BOLA flaw.
For example, middleBrick might send requests like:
GET /api/profiles/123 (returns user A's data)
GET /api/profiles/124 (returns user B's data)
If both requests succeed without authentication or with a low-privilege token, the scanner flags this as a high-severity BOLA finding. The report includes the exact endpoint, parameter tested, and evidence (response snippets).
Developers can manually replicate this by:
- Identifying API endpoints that accept identifiers (IDs, emails, tokens) as path or query parameters.
- Using tools like
curlor Postman to send requests with sequential or common values (e.g.,1,2,admin,[email protected]). - Observing whether the response leaks data from other users. A 403/401 is expected; a 200 with unrelated data indicates a vulnerability.
middleBrick automates this at scale, testing dozens of parameter combinations in parallel within its 5–15 second scan, and maps findings to OWASP API Top 10:2023 Broken Object Level Authorization (BOLA).
Firestore-Specific Remediation
Remediation centers on enforcing strict access controls in Firestore Security Rules and ensuring IDs are non-guessable. Never rely solely on client-side checks.
1. Use Non-Predictable Document IDs
Generate UUIDs or cryptographically random strings for document IDs instead of sequential numbers or user emails. In Node.js with Firebase Admin:
const { v4: uuidv4 } = require('uuid');
const orderId = uuidv4(); // e.g., "f47ac10b-58cc-4372-a567-0e02b2c3d479"
await db.collection('users').doc(userId).collection('orders').doc(orderId).set(orderData);
This makes enumeration infeasible. If using email as an ID, hash it first: const hashedEmail = crypto.createHash('sha256').update(email).digest('hex');.
2. Harden Firestore Security Rules
Rules must validate that the requesting user owns the target document. Example for an orders subcollection:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId}/orders/{orderId} {
allow read, write: if request.auth != null && request.auth.uid == userId;
}
}
}
For queries (e.g., fetching all orders for a user), ensure the query is scoped:
allow list: if request.auth != null && request.query.where('userId', '==', request.auth.uid);
Never use allow read: if true; or allow read: if request.auth != null; without checking ownership.
3. Validate Inputs at the API Layer
Even with strong rules, validate parameters in your API before passing them to Firestore. Reject non-UUID formats or malformed emails:
app.get('/api/orders/:orderId', async (req, res) => {
if (!isValidUUID(req.params.orderId)) {
return res.status(400).json({ error: 'Invalid order ID' });
}
// ... fetch with Firestore
});
Combine with Firestore rules for defense-in-depth.
4. Implement Rate Limiting
Dictionary attacks rely on high request volumes. Use middleware like express-rate-limit to throttle requests per IP/user:
const rateLimit = require('express-rate-limit');
app.use('/api/orders/', rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100 // limit each IP to 100 requests per windowMs
}));
This doesn't prevent determined attackers but raises the cost.
After applying fixes, rescan with middleBrick to verify the BOLA/IDOR check no longer flags the endpoint. The Pro plan's continuous monitoring can alert if rules regress.
Frequently Asked Questions
Does Firestore's default security mode prevent dictionary attacks?
allow read, write: if request.auth != null;), which does not prevent authenticated users from accessing others' data via guessable IDs. You must write custom rules that enforce per-user ownership checks.How does middleBrick test for dictionary attacks without valid credentials?
/api/users/1 vs /api/users/2) without proper authorization, it flags the vulnerability. This mirrors an attacker's first steps before obtaining credentials.