Http Request Smuggling in Express with Firestore
Http Request Smuggling in Express with Firestore — how this specific combination creates or exposes the vulnerability
Http Request Smuggling can occur in an Express application that uses Firestore when request parsing and routing are inconsistent between frontend (e.g., a reverse proxy or load balancer) and the Express backend. Firestore is often used via the official Node.js SDK and is typically accessed through authenticated client instances initialized with project credentials. If Express routes handle raw bodies or multiple Content-Length/Transfer-Encoding headers differently than the proxy, an attacker can craft requests where the proxy and Express interpret message boundaries differently. This can cause one request’s body to be smuggled into another request, potentially reaching routes that interact with Firestore.
For example, an Express route that uses body-parser for JSON while also handling URL-encoded data can mis-parse a request containing both Content-Length and Transfer-Encoding: chunked. If the proxy processes one header set and Express processes another, the second request might be interpreted as part of the first request’s body. If that smuggled request targets an endpoint that calls Firestore (for instance, a GET /users/:id that uses doc.get()), the attacker can make unauthorized Firestore reads or influence writes by injecting headers or body fragments that change the intended Firestore document path or parameters.
Firestore-specific risks arise when smuggled requests modify how documents are accessed or which documents are accessed. Consider an Express route that builds a Firestore document reference from user-supplied input without strict validation:
const { doc, getDoc } = require('firebase/firestore');
app.get('/posts/:postId/comments/:commentId', async (req, res) => {
const { postId, commentId } = req.params;
const docRef = doc(db, 'posts', postId, 'comments', commentId);
const snap = await getDoc(docRef);
if (snap.exists()) {
res.json(snap.data());
} else {
res.status(404).send('Not found');
}
});
If a smuggled request manipulates postId or commentId via injected headers or body fragments, the backend may inadvertently access or expose a different Firestore document. Because Firestore enforces security rules at the document level, smuggling that changes the document path can bypass intended access controls, leading to unauthorized data exposure or writes if the backend also performs operations based on parsed headers without validating them independently.
To detect this category of issue, scanning with middleBrick is valuable because it tests unauthenticated attack surfaces and includes checks for Input Validation and BOLA/IDOR, which can surface inconsistent handling of requests that may enable smuggling. middleBrick’s scans provide prioritized findings with severity and remediation guidance, helping you identify risky routing or parsing combinations before they are exploited in production.
Firestore-Specific Remediation in Express — concrete code fixes
Remediation focuses on ensuring consistent request parsing and strict validation of all inputs used to construct Firestore references. Use a single, well-configured body parser and avoid mixing raw body handling with JSON or urlencoded parsing. Apply strict validation on route parameters and headers before using them to build Firestore document paths.
First, standardize body parsing with express.json() and avoid duplicate parsing:
const express = require('express');
const bodyParser = require('body-parser');
const { initializeApp } = require('firebase-admin');
const { getFirestore, doc, getDoc } = require('firebase/firestore');
const app = express();
app.use(bodyParser.json({ limit: '1mb' }));
app.use(bodyParser.urlencoded({ extended: true }));
// Ensure no raw body middleware that conflicts with body-parser
Second, validate and sanitize route parameters before using them in Firestore operations. Reject non-expected patterns and enforce type checks:
app.get('/posts/:postId/comments/:commentId', async (req, res) => {
const { postId, commentId } = req.params;
if (!/^[a-zA-Z0-9_-]{1,30}$/.test(postId) || !/^[a-zA-Z0-9_-]{1,30}$/.test(commentId)) {
return res.status(400).send('Invalid parameter format');
}
const docRef = doc(db, 'posts', postId, 'comments', commentId);
const snap = await getDoc(docRef);
if (snap.exists()) {
res.json(snap.data());
} else {
res.status(404).send('Not found');
}
});
Third, avoid deriving Firestore paths from untrusted headers. If you must use headers, validate them strictly and do not allow them to override path components. For example, if you accept a X-Document-Path header, do not directly concatenate it; instead, map it to an allowlist of known paths or use it only for metadata, not for constructing document references.
Fourth, configure your reverse proxy or load balancer to normalize or strip ambiguous Transfer-Encoding and Content-Length headers before requests reach Express. This reduces the risk of mismatched parsing. Combine this with middleware that rejects requests with both Content-Length and Transfer-Encoding headers to reduce smuggling surface:
app.use((req, res, next) => {
if (req.headers['content-length'] && req.headers['transfer-encoding']) {
return res.status(400).send('Conflicting headers');
}
next();
});
Finally, use middleBrick’s scans to verify that your configuration and validation logic reduce the attack surface. The tool’s Input Validation and BOLA/IDOR checks can highlight weak parameter handling and overly permissive routing, and its CLI can be integrated into CI/CD to catch regressions early via the GitHub Action, which fails builds if security scores drop below your chosen threshold.