Token Leakage in Firestore
How Token Leakage Manifests in Firestore
Token leakage in Firestore typically occurs when authentication tokens, session identifiers, or database keys are inadvertently exposed in client-side code, API responses, or error messages. Firestore's client libraries and security rules create specific vulnerability patterns that attackers can exploit.
The most common Firestore token leakage scenarios involve:
- Firestore ID tokens exposed in browser console logs or network requests
- Document IDs containing sensitive information that become predictable
- Security rules that inadvertently reveal whether documents exist
- Collection names that expose business logic or user data structure
- Metadata fields containing timestamps or counters that enable enumeration
Consider this vulnerable Firestore pattern:
const db = firebase.firestore();
const userDoc = db.collection('users').doc(userId);
userDoc.get().then(doc => {
if (doc.exists) {
console.log('User found:', userId); // Potential token leakage via console
return doc.data();
} else {
console.log('User not found:', userId); // Enumeration possible
return null;
}
});This code leaks information through timing differences and console output. An attacker can distinguish between valid and invalid user IDs based on response times and error messages.
Another critical pattern is Firestore security rules that reveal document existence:
match /users/{userId} {
allow read: if request.auth.uid == userId;
allow write: if request.auth.uid == userId;
}
match /users/{userId} {
allow read: if request.auth.uid == userId;
allow write: if request.auth.uid == userId;
}
match /users/{userId} {
allow read: if request.auth.uid == userId;
allow write: if request.auth.uid == userId;
}Even though the rules look secure, the HTTP 404 vs 403 response codes leak whether a user document exists, enabling account enumeration attacks.
Firestore's client-side SDK also creates token leakage opportunities through:
- Default error messages containing stack traces with database paths
- Query cursors that reveal data boundaries
- Transaction IDs that can be correlated across requests
- Collection group queries that expose the full data graph
Firestore-Specific Detection
Detecting token leakage in Firestore requires both static analysis of your security rules and dynamic scanning of your API endpoints. middleBrick's Firestore-specific scanner examines your Firestore configuration and runtime behavior to identify these vulnerabilities.
middleBrick performs these Firestore-specific checks:
- Security Rules Analysis: Parses your Firestore security rules to identify overly permissive patterns, missing validation, and information disclosure through error responses
- Token Exposure Scanning: Tests whether authentication tokens or session identifiers appear in client-side code, network responses, or error messages
- Enumeration Detection: Attempts to enumerate document IDs by analyzing response timing and error codes
- Metadata Leakage: Checks for exposed timestamps, counters, or other metadata that could enable data reconstruction
Using middleBrick's CLI for Firestore scanning:
npx middlebrick scan https://firestore.googleapis.com/projects/YOUR_PROJECT_ID/databases/(default)/documents
The scanner tests your Firestore endpoint with 12 parallel security checks, including authentication bypass attempts and data exposure analysis. It specifically looks for Firestore's unique token leakage patterns:
| Check Type | Firestore-Specific Pattern | Detection Method |
|---|---|---|
| Authentication | ID token exposure in client code | Static analysis of JavaScript bundles |
| BOLA/IDOR | Predictable document IDs | Enumeration attempts with sequential IDs |
| Data Exposure | Metadata field leakage | Response content analysis |
| Inventory Management | Collection structure exposure | Discovery of hidden collections |
For comprehensive Firestore security, middleBrick also analyzes your OpenAPI/Swagger specifications if provided, cross-referencing API endpoints with your Firestore data model to identify potential token leakage paths between your REST API and Firestore backend.
Firestore-Specific Remediation
Remediating token leakage in Firestore requires a multi-layered approach combining secure coding practices, robust security rules, and careful client-side implementation. Here are Firestore-specific fixes for common token leakage patterns.
1. Secure Security Rules with Generic Error Responses
Instead of revealing document existence through different error codes, use consistent responses:
match /users/{userId} {
allow read: if request.auth.uid == userId;
allow write: if request.auth.uid == userId;
}
match /users/{userId} {
allow read: if request.auth.uid == userId;
allow write: if request.auth.uid == userId;
}
match /users/{userId} {
allow read: if request.auth.uid == userId;
allow write: if request.auth.uid == userId;
}2. Client-Side Code Protection
Sanitize console output and handle errors without exposing sensitive data:
const db = firebase.firestore();
const userDoc = db.collection('users').doc(userId);
userDoc.get()
.then(doc => {
if (doc.exists) {
return { success: true, data: doc.data() };
} else {
return { success: false, data: null };
}
})
.catch(error => {
console.error('Database operation failed'); // Generic message
return { success: false, error: 'Operation failed' };
});3. Use Firestore's Built-in Security Features
Leverage Firestore's security features to prevent token leakage:
// Use Cloud Functions to validate and sanitize data
exports.sanitizeUserData = functions.firestore
.document('users/{userId}')
.onWrite((change, context) => {
const newData = change.after.data();
// Remove sensitive metadata before storing
if (newData.metadata) {
delete newData.metadata;
}
return change.after.ref.set(newData, { merge: true });
});4. Implement Proper Authentication Token Handling
Ensure Firestore ID tokens are properly secured:
// Generate tokens only when needed and expire them quickly
const getIdToken = async () => {
const user = firebase.auth().currentUser;
if (!user) {
throw new Error('User not authenticated');
}
// Set a short expiration time
return await user.getIdToken(true, {
expiresIn: '1m'
});
};
// Never log tokens or expose them in client-side code
getIdToken().then(token => {
// Use token immediately for API calls
fetch('/api/secure-endpoint', {
headers: { 'Authorization': `Bearer ${token}` }
});
});5. Use Firestore's Aggregation Queries
Instead of client-side enumeration, use server-side aggregation:
// Server-side Cloud Function to safely count documents
exports.countUsers = functions.https.onRequest(async (req, res) => {
try {
const count = await admin.firestore()
.collection('users')
.count()
.get();
res.json({ userCount: count });
} catch (error) {
res.status(500).json({ error: 'Counting failed' });
}
});