Request Smuggling in Firestore
How Request Smuggling Manifests in Firestore
Request smuggling traditionally describes discrepancies in HTTP request parsing between intermediaries (proxies, load balancers) and backend servers. In Firestore's context, this manifests through malformed or ambiguous requests to its REST or gRPC APIs that are interpreted differently by client libraries, middleware, or Firestore's own parsing logic, leading to partial execution, data corruption, or unauthorized access.
Firestore's query language and write operations (batched writes, transactions) rely on precise JSON or protocol buffer structures. A smuggled request typically exploits:
- Ambiguous operator injection: Injecting additional query operators (e.g.,
!=,not-in) or array filters that alter query semantics if not strictly validated. For example, a malicious actor might craft a query string like?where=field%3Dvalue&where=field2%3Dvalue2where the secondwhereparameter is ignored by some parsers but processed by others, narrowing or broadening the result set unexpectedly. - Batched write boundary manipulation: Firestore allows up to 500 operations in a single batched write request. A smuggled request might use malformed JSON or incorrect
Content-Lengthheaders to cause the server to process only a subset of operations, while the client believes all were applied. This can lead to inconsistent state if an attacker can drop specific operations (e.g., a permission check) while executing others (e.g., a data delete). - gRPC message framing attacks: When using the gRPC API (common in mobile/backend services), improper handling of gRPC frame boundaries could allow an attacker to prepend or append operations that are ignored by some layers but processed by Firestore. This is rare but possible in custom proxy setups.
Consider this vulnerable Node.js snippet using the Firestore SDK that builds a query from user input without normalization:
// VULNERABLE: Direct concatenation of user input into query structure
const userInput = req.query.filter; // e.g., "name=Alice&age!=30"
const parts = userInput.split('&');
let query = db.collection('users');
parts.forEach(part => {
const [field, value] = part.split('=');
// No validation of field names or operators
query = query.where(field, '==', value);
});
const snapshot = await query.get();
An attacker could supply ?filter=name==Alice&age!=30. If the SDK's internal parser treats the second != as an invalid operator and silently drops it, the query returns all users named Alice regardless of age, whereas the attacker intended to exclude age 30. This discrepancy is a form of request smuggling where the intended filter is partially lost.
Firestore-Specific Detection
Detecting request smuggling in Firestore requires testing for inconsistencies between the expected behavior defined in your API specification (OpenAPI/Swagger) and the actual runtime behavior. middleBrick's scanner performs this by sending crafted payloads to your Firestore REST/gRPC endpoints and analyzing responses for signs of partial processing, ignored parameters, or state inconsistencies.
Key detection patterns include:
- Parameter shadowing tests: Send duplicate query parameters (e.g., multiple
whereclauses) and check if the response reflects only the first, last, or a merged set. A smuggled request may cause the backend to use a different subset than what the client library sent. - Batched write truncation probes: Submit a batched write with 5 operations but intentionally corrupt the JSON structure after the third operation (e.g., missing comma, extra brace). If the server executes only the first three while the client library believes all five succeeded, this indicates a parsing discrepancy.
- Operator fuzzing: Send queries with unsupported operators (e.g.,
not-inin a context that only supports==) and observe whether they are silently ignored or cause errors. Silent ignoring can lead to smuggled logic.
Using middleBrick, you can scan any Firestore endpoint (REST or gRPC) without credentials. The scanner will:
- Analyze your OpenAPI spec (if available) to understand expected parameters and operations.
- Launch parallel probes that test for parameter shadowing, batched write anomalies, and operator handling.
- Assign a risk score (A–F) and provide specific findings, such as: "Batched write request with malformed JSON after 3rd operation resulted in partial execution (3/5 operations applied). This indicates potential request smuggling via Content-Length mismatch."
Example CLI usage to scan a Firestore REST endpoint:
middlebrick scan https://firestore.googleapis.com/v1/projects/your-project/databases/(default)/documents/users
The report will highlight if your endpoint exhibits behaviors consistent with request smuggling, mapped to OWASP API Top 10 categories like API4:2023 – Unrestricted Resource Consumption (if smuggling enables DoS) or API5:2023 – Broken Function Level Authorization (if it bypasses intended query constraints).
Firestore-Specific Remediation
Remediation focuses on ensuring strict, consistent request parsing and validation at the application layer, since Firestore's backend itself is not vulnerable to traditional HTTP smuggling—the risk arises in how your application constructs and interprets requests to Firestore. Use Firestore's native features and client libraries to enforce atomicity and validation.
1. Normalize and validate all query inputs: Never trust client-supplied query structures. Parse and sanitize user input before applying it to a Firestore query. Use allowlists for field names and operators.
// SECURE: Validate and normalize user input
const ALLOWED_FIELDS = ['name', 'email', 'age'];
const ALLOWED_OPERATORS = ['==', '>', '<', 'array-contains'];
function buildSecureQuery(filterString) {
const query = db.collection('users');
const parts = filterString.split('&');
for (const part of parts) {
const [field, op, value] = part.split(/[:=]/);
if (!ALLOWED_FIELDS.includes(field) || !ALLOWED_OPERATORS.includes(op)) {
throw new Error('Invalid query parameter');
}
// Use parameterized queries where possible
query = query.where(field, op, value);
}
return query;
}
// Usage
const userFilter = req.query.filter || 'name==Alice';
const snapshot = await buildSecureQuery(userFilter).get();
2. Use batched writes with atomic error handling: Firestore batched writes are atomic—either all operations succeed or none apply. However, if your application layer modifies the request before sending, ensure the entire batch is validated as a unit. Do not split or reorder operations based on client input.
// SECURE: Validate entire batch structure before committing
const batch = db.batch();
const operations = req.body.operations; // Expect array of {op, path, value}
if (operations.length > 500) {
throw new Error('Batch size exceeds limit');
}
operations.forEach(op => {
const ref = db.collection('logs').doc();
if (op.type === 'create') {
batch.set(ref, op.data);
} else if (op.type === 'update') {
batch.update(ref, op.data);
} // ... other types
});
// Commit atomically; if any operation is invalid, none apply
try {
await batch.commit();
} catch (e) {
// Log and return consistent error
console.error('Batch failed:', e);
res.status(400).json({error: 'Invalid batch operation'});
}
3. Enforce strict Content-Type and length validation: For REST endpoints, reject requests with missing or incorrect Content-Type: application/json headers. Also, set a maximum request body size (Firestore limits apply, but your proxy should enforce tighter bounds) to prevent framing attacks.
// Express middleware example
app.use(express.json({
limit: '1mb', // Well below Firestore's 10MB limit
strict: true // Reject duplicate keys in JSON
}));
4. Use Firestore security rules as a second layer: While not a direct fix for smuggling, rules ensure that even if a query is malformed, the data access is still constrained. For example, enforce that queries on users collection must filter on tenantId matching the requester's tenant.
// Firestore security rules
match /users/{userId} {
allow read: if request.query.where('tenantId') == request.auth.tenantId;
// This forces every query to include tenantId filter
}
Regularly scan your Firestore endpoints with middleBrick to verify that these controls are effective and that no new smuggling vectors emerge from client library upgrades or API changes.
Understanding the Risk: Why This Matters for Firestore APIs
Request smuggling in Firestore APIs may seem niche, but it can lead to severe impacts:
- Data corruption: Partial batched writes could leave some documents updated while others are not, violating business logic (e.g., transferring funds without deducting from source).
- Authorization bypass: If a query filter is dropped, an attacker might read documents from other tenants or users.
- Denial-of-service: Malformed requests that cause excessive parsing overhead or partial state can degrade performance.
These issues map directly to compliance frameworks. For example, PCI-DSS Requirement 6.5.1 (injection flaws) and OWASP API4:2023 (unrestricted resource consumption) are relevant. middleBrick's scoring includes these mappings, helping you prioritize fixes for compliance.
Unlike traditional web request smuggling (e.g., HTTP/1.1 Transfer-Encoding: chunked vs Content-Length conflicts), Firestore smuggling is about semantic discrepancies in query/write structures. The attack surface is smaller but high-impact because Firestore often holds critical application data.