Header Injection in Firestore
How Header Injection Manifests in Firestore
Header injection in Firestore contexts typically occurs when user-controlled data flows through HTTP headers and gets passed to Firestore operations without proper validation. This vulnerability can manifest in several Firestore-specific scenarios.
The most common pattern involves HTTP headers containing collection names, document IDs, or field names that get directly interpolated into Firestore queries. For example, an API endpoint might accept a custom header like X-Collection-Name to determine which Firestore collection to query. If an attacker sets this header to a sensitive collection name, they can access data they shouldn't see.
// VULNERABLE: Header injection via collection name
const collectionName = req.headers['x-collection-name'] || 'public';
const snapshot = await db.collection(collectionName).get();
Another variant involves document ID injection where header values determine which document to retrieve:
// VULNERABLE: Header injection via document ID
const docId = req.headers['x-document-id'];
const doc = await db.collection('users').doc(docId).get();
Field name injection is subtler but equally dangerous. When field names are derived from headers, attackers can manipulate which data gets returned:
// VULNERABLE: Header injection via field name
const fieldName = req.headers['x-field-name'] || 'email';
const doc = await db.collection('users').doc(userId).get();
const fieldValue = doc.get(fieldName); // Arbitrary field access
Firestore's flexible schema makes this particularly risky. Unlike traditional databases with fixed schemas, Firestore allows any field name, so validation becomes critical. Attackers can exploit this to access hidden fields, bypass authorization checks, or trigger unintended query behavior.
Query parameter injection through headers is another attack vector. Firestore queries accept complex filter objects, and headers can be used to construct malicious query parameters:
// VULNERABLE: Header injection via query parameters
const filters = JSON.parse(req.headers['x-filters'] || '{}');
const query = db.collection('users');
if (filters.status) query = query.where('status', '==', filters.status);
This allows attackers to craft filters that access data they shouldn't see or cause denial-of-service through expensive queries.
Firestore-Specific Detection
Detecting header injection in Firestore applications requires both static analysis and runtime scanning. The most effective approach combines automated tools with manual code review.
Static analysis should focus on identifying patterns where HTTP headers are used to construct Firestore operations. Look for:
- Direct interpolation of header values into collection names, document IDs, or field names
- JSON parsing of header values that could contain malicious data
- Dynamic query construction based on header input
- Lack of whitelist validation for header-derived values
- Missing authorization checks after header-based operations
middleBrick's API security scanner specifically detects header injection vulnerabilities in Firestore contexts. It tests for common injection patterns by:
- Modifying header values to access sensitive collections
- Attempting to traverse document hierarchies using crafted header inputs
- Testing for field name injection by requesting non-standard fields
- Analyzing the application's response to malformed header values
The scanner evaluates whether the application properly validates and sanitizes header inputs before using them in Firestore operations. It checks for common anti-patterns like using headers as collection names or document IDs without validation.
Runtime detection involves monitoring API calls for suspicious header patterns. Tools like middleBrick can automatically scan your Firestore endpoints and identify header injection vulnerabilities in seconds. The scanner tests the unauthenticated attack surface and provides a security risk score with actionable findings.
For comprehensive coverage, combine automated scanning with manual testing. Use tools like Postman or curl to craft malicious header payloads and observe how the application responds. Test edge cases like extremely long header values, special characters, and unexpected data types.
middleBrick's continuous monitoring feature (Pro plan) can automatically rescan your Firestore endpoints on a schedule, alerting you to new vulnerabilities as they're introduced during development.
Firestore-Specific Remediation
Remediating header injection in Firestore requires a defense-in-depth approach. The most effective strategy combines strict validation, proper authorization, and architectural best practices.
First, implement strict input validation for all header-derived values. Never trust header contents and always validate against a whitelist of allowed values:
// SECURE: Whitelist validation for collection access
const allowedCollections = ['public', 'public-users', 'public-products'];
const collectionName = req.headers['x-collection-name'];
if (!allowedCollections.includes(collectionName)) {
return res.status(400).json({ error: 'Invalid collection' });
}
const snapshot = await db.collection(collectionName).get();
For document access, validate document IDs against expected patterns and always perform authorization checks:
// SECURE: Validate and authorize document access
const docId = req.headers['x-document-id'];
if (!isValidDocumentId(docId)) {
return res.status(400).json({ error: 'Invalid document ID' });
}
// Always check authorization before accessing documents
const doc = await db.collection('users').doc(docId).get();
if (!await hasPermission(req.user, doc)) {
return res.status(403).json({ error: 'Access denied' });
}
Field name injection requires similar validation. Define explicit field mappings rather than allowing arbitrary field names:
// SECURE: Explicit field mapping instead of injection
const fieldMap = {
'email': 'email',
'username': 'username',
'profile': 'profile'
};
const requestedField = req.headers['x-field-name'];
const fieldName = fieldMap[requestedField];
if (!fieldName) {
return res.status(400).json({ error: 'Invalid field' });
}
const fieldValue = doc.get(fieldName);
Query parameter injection is best prevented by using parameterized queries and avoiding dynamic query construction:
// SECURE: Parameterized queries with validation
const filters = JSON.parse(req.headers['x-filters'] || '{}');
const query = db.collection('users');
// Validate each filter parameter
const allowedFilters = ['status', 'role', 'active'];
Object.keys(filters).forEach(key => {
if (!allowedFilters.includes(key)) {
throw new Error('Invalid filter parameter');
}
query = query.where(key, '==', filters[key]);
});
Consider using Firestore security rules as an additional layer of protection. Even if header injection occurs, security rules can prevent unauthorized data access:
// Firestore security rules example
match /collections/{collectionId} {
allow read: if request.auth != null &&
collectionId in allowedCollections &&
hasPermission(request.auth.uid, resource);
}
Finally, implement comprehensive logging and monitoring. Track all header-derived operations and alert on suspicious patterns like access to sensitive collections or unusual query patterns.