Request Smuggling in Feathersjs with Api Keys
Request Smuggling in Feathersjs with Api Keys — how this specific combination creates or exposes the vulnerability
Request smuggling occurs when an application processes the same HTTP request differently in transit versus at the backend, often due to inconsistent or incomplete parsing of headers, chunked encoding, or transfer-encodings. When using FeathersJS with API keys, the risk is introduced at the integration point where client-supplied keys (typically via headers such as x-api-key) are handled before the request reaches application-level routing and services. FeathersJS itself is a framework-agnostic layer; if you mount it behind a reverse proxy, API gateway, or load balancer that parses headers differently, and you rely on header-based API keys for routing or authorization, you can create a smuggling surface.
Consider a setup where an external client sends:
POST /v1/users HTTP/1.1
Content-Length: 57
Transfer-Encoding: chunked
x-api-key: my-secret-key
0
A front-end component might normalize or strip Transfer-Encoding while preserving x-api-key, causing the proxy to route using the key while FeathersJS (or an intermediary) processes the body according to a different message boundary. Because FeathersJS services rely on feathers-hooks-common and custom hooks for authorization, the key may be validated and considered trusted before the request’s true method and path are fully resolved. This mismatch can allow an attacker to smuggle a request so that one request’s body (e.g., an admin-level operation) is interpreted as belonging to another request path or connection, bypassing intended isolation.
The vulnerability is compounded when API keys are used for routing decisions (e.g., versioning or tenant identification) rather than strictly for authentication. If hooks or middleware derive routing parameters from headers before the framework’s service pipeline stabilizes the request, an attacker can exploit parser ambiguities to route a smuggled request to a privileged endpoint. Real-world patterns seen in OWASP API Top 10 (e.g., Broken Object Level Authorization) can be triggered this way, where a smuggled request performs an action the original sender was not authorized to make.
middleBrick scans such setups by running checks for BOLA/IDOR, BFLA/Privilege Escalation, and Unsafe Consumption in parallel, identifying whether header-based routing combined with API keys creates an inconsistent attack surface. The scanner does not fix the issue but provides prioritized findings with remediation guidance to help you close the parsing inconsistency.
Api Keys-Specific Remediation in Feathersjs — concrete code fixes
To mitigate request smuggling when using API keys in FeathersJS, ensure strict header parsing, avoid early trust of mutable headers, and enforce consistent message boundaries. Below are concrete, syntactically correct examples demonstrating secure patterns.
1. Explicitly validate and normalize headers before hook execution
In Feathers, hooks run per-service or globally. Place a hook that validates the presence and exact value of x-api-key before any routing or authorization logic. Do not rely on headers that may be modified or duplicated by intermediaries.
// src/hooks/validate-api-key.hooks.js
const validateApiKey = (options = {}) => {
return async context => {
const { app, params } = context;
const apiKey = context.params.headers['x-api-key'];
const expected = process.env.API_KEY_SECRET;
if (!apiKey || apiKey !== expected) {
throw new Error('Unauthorized: Invalid API key');
}
// Remove the key from headers to prevent accidental propagation or re-use
delete context.params.headers['x-api-key'];
return context;
};
};
module.exports = {
before: {
all: [validateApiKey()],
find: [],
get: [],
create: [],
update: [],
patch: [],
remove: []
}
};
Register the hook in your service configuration:
// src/services/users/users.service.js
const { Service } = require('feathersjs');
const validateApiKey = require('../hooks/validate-api-key.hooks');
class UsersService extends Service {
constructor(options) {
super(options);
}
}
module.exports = function () {
const app = this;
const options = {
name: 'users',
Model: app.get('UserModel')
};
const service = new UsersService(options);
service.hooks({
before: validateApiKey().hooks.before
});
return service;
};
2. Enforce strict Content-Length and reject ambiguous Transfer-Encoding
Configure your HTTP layer (e.g., Express or a reverse proxy) to reject requests that mix Content-Length and Transfer-Encoding. In Feathers, this is typically done at the adapter or server setup level:
// src/server/express.js
const express = require('express');
const feathers = require('@feathersjs/feathers');
const middleware = require('@feathersjs/express/middleware');
const app = feathers();
app.use(express.json({ strict: true })); // strict parsing rejects ambiguous encodings
app.use(express.urlencoded({ extended: false, strict: true }));
app.use(middleware.rest());
app.use(middleware.slash());
app.use(middleware.contentCompression());
// Ensure no duplicate or malformed headers are processed
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('Bad Request: Confused Deputy — ambiguous encoding headers');
}
next();
});
module.exports = app;
3. Use framework-agnostic routing and avoid header-based routing keys
Instead of using API keys to select routes or tenants at the proxy or within Feathers hooks before service execution, centralize routing within Feathers and perform authorization late, after parsing is stable. Example of safe tenant resolution using a verified identity rather than raw headers:
// src/hooks/tenant-resolver.hooks.js
const resolveTenant = (options = {}) => {
return async context =>
context.params.tenant = {
id: context.params.user.tenantId,
canAccess: (resource) => context.params.user.permissions.includes(resource)
};
};
module.exports = {
before: {
all: [resolveTenant()]
}
};
These patterns reduce the risk of request smuggling by ensuring that API keys are strictly validated and removed early, headers are normalized consistently, and routing decisions are made with a stable, fully parsed request.