Request Smuggling in Feathersjs with Firestore
Request Smuggling in Feathersjs with Firestore — how this specific combination creates or exposes the vulnerability
Request smuggling arises when an API processes HTTP requests in a way that allows an attacker to smuggle a malicious request to a backend handler, bypassing intended access controls or causing requests to be interpreted differently by front-end and back-end servers. FeathersJS, a popular framework for real-time applications, typically sits behind a reverse proxy or load balancer and uses a layered service architecture. When FeathersJS services interact with external data stores such as Google Cloud Firestore, smuggling can occur if request parsing, routing, or proxy chaining misaligns between layers.
In a FeathersJS application using Firestore, smuggling can be exposed through two common conditions:
- Inconsistent HTTP message parsing between the proxy and FeathersJS, especially when one layer uses chunked transfer encoding while the other does not.
- Service routes that accept nested bodies or rely on raw request streams that, when combined with Firestore document reads/writes, allow an attacker to inject an additional request path or method into the smuggling gap.
For example, if a reverse proxy normalizes headers before forwarding to FeathersJS, but FeathersJS (or an Express underlying layer) does not enforce strict header separation, an attacker can craft a request like GET /v1/users HTTP/1.1\r\nContent-Length: 36\r\n\r\nGET /admin/export HTTP/1.1\r\nHost: api.example.com where the first request is interpreted one way and the smuggled request another. Because Firestore operations often rely on authenticated context and well-formed document paths, a smuggled request could attempt unauthorized document access or trigger unintended write paths if route authorization is not consistently enforced at the FeathersJS service layer.
Firestore-specific risk patterns in FeathersJS include:
- Services that accept arbitrary JSON and directly construct document references without validating path components, enabling path traversal via smuggling to access or overwrite documents belonging to other users.
- Webhook or external trigger endpoints that use Firestore listeners and are exposed through FeathersJS routes, where smuggling can manipulate which Firestore document events are processed.
Because FeathersJS does not inherently validate that the request context aligns with the intended Firestore security rules scope, smuggling can bypass application-level authorization, making it critical to enforce strict header handling and route isolation.
Firestore-Specific Remediation in Feathersjs — concrete code fixes
Remediation focuses on aligning request parsing behavior between FeathersJS and any fronting proxy, validating all user-controlled inputs before Firestore interactions, and ensuring route-level authorization is applied consistently.
1. Enforce strict Content-Length and Transfer-Encoding parsing in your FeathersJS app so that smuggled encoding mismatches are rejected. Configure your underlying HTTP server to reject requests with ambiguous message framing.
// server.js — explicit parsing configuration
const feathers = require('@feathersjs/feathers');
const express = require('@feathersjs/express');
const app = express(feathers());
// Reject requests with both Transfer-Encoding and Content-Length
app.use((req, res, next) => {
const te = req.headers['transfer-encoding'];
const cl = req.headers['content-length'];
if (te && cl) {
return res.status(400).send('Invalid message framing');
}
next();
});
app.use('/', express.static());
app.configure(express.rest());
app.configure(express.socketio());
2. Validate and sanitize all parameters used to construct Firestore document paths to prevent traversal via smuggling. Use allowed patterns and reject paths containing .. or leading slashes that could escape intended collections.
// services/users/users.service.js — safe document reference
const { Firestore } = require('@google-cloud/firestore');
const firestore = new Firestore();
class UsersService {
async get(id, params) {
if (!/^[a-zA-Z0-9_-]+$/.test(id)) {
throw new Error('Invalid document ID');
}
const docRef = firestore.collection('users').doc(id);
const doc = await docRef.get(params.firestore || {});
if (!doc.exists) {
throw new Error('Not found');
}
return doc.data();
}
}
module.exports = function () {
const app = this;
app.use('/users', new UsersService());
};
3. Apply per-request authorization checks in FeathersJS hooks so that even if smuggling alters the intended route, Firestore operations still validate ownership and scope. Do not rely solely on Firestore security rules for routing-layer authorization.
// hooks/authentication.hooks.js — ownership verification before Firestore access
const { Firestore } = require('@google-cloud/firestore');
const firestore = new Firestore();
exports.ensureOwnership = async context => {
const { user, params, result } = context;
if (!user || !user.uid) {
throw new Error('Unauthorized');
}
// For document-specific routes, verify the document belongs to the user
if (context.id) {
const doc = await firestore.collection(context.path === 'users' ? 'users' : 'items')
.doc(context.id).get();
if (!doc.exists || doc.get('userId') !== user.uid) {
throw new Error('Access denied to this document');
}
}
return context;
};
4. Use explicit route definitions and avoid catch-all or wildcard route patterns that can be exploited to smuggle requests to unintended Firestore paths. Prefer strongly typed service paths and parameter validation.
// app.js — explicit service registration
module.exports = function () {
const app = this;
app.use('/api/users', require('./services/users/users.service').default);
app.use('/api/items', require('./services/items/items.service').default);
// Not recommended: app.use('/', require('./services/dynamic.service').default);
};