Http Request Smuggling in Feathersjs with Hmac Signatures
Http Request Smuggling in Feathersjs with Hmac Signatures — how this specific combination creates or exposes the vulnerability
HTTP Request Smuggling arises from inconsistent parsing of requests between security layers (e.g., a reverse proxy) and the Feathersjs application. When Hmac Signatures are used for request authentication, smuggling can occur if the body transformation or Content-Length/Transfer-Encoding handling differs between the proxy and the Feathersjs service, allowing an attacker to smuggle a second request inside or across requests.
Feathersjs does not enforce a strict message length or framing policy by default. If a proxy normalizes or modifies headers such as Content-Length or Transfer-Encoding before passing the request to Feathersjs, and Feathersjs parses the body differently, an attacker can craft a request where the smuggled portion is interpreted as a new request by the next hop. For example, a client may sign a request with an Hmac that covers a specific Content-Length; a proxy might forward the body with a different length, causing Feathersjs to read the surplus data as a new request that bypasses intended authentication or authorization checks.
Hmac Signatures introduce a canonicalization risk: the signature is typically computed over selected headers and the body. If the smuggling layer alters headers that are included in the signature (such as Content-Length, Transfer-Encoding, or custom headers) without the signer’s knowledge, the signature may still verify at Feathersjs if the application does not strictly validate header consistency before computing or verifying the Hmac. This can lead to a situation where a smuggled request is accepted with a valid-looking signature, enabling unauthorized operations or data access.
An example scenario: A client sends POST /v1/users with body { "name": "alice" } and an Hmac covering Content-Length: 19. A proxy normalizes the request, changes the encoding, and forwards it with an additional smuggled request POST /v1/accounts appended. Feathersjs, if not enforcing strict header and body alignment, may process the smuggled request with the same signature context or incorrectly parse the body, leading to security bypass or injection of unintended operations.
To detect this category during a scan, middleBrick runs checks that compare runtime behavior against the OpenAPI/Swagger spec (including $ref resolution) and inspect how headers and body are handled. The tool tests for inconsistencies in Content-Length and Transfer-Encoding parsing, and flags cases where Hmac-verified requests do not enforce strict header canonicalization, helping to identify potential smuggling surfaces in Feathersjs services that use Hmac Signatures.
Hmac Signatures-Specific Remediation in Feathersjs — concrete code fixes
Remediation focuses on canonicalizing headers before signature computation, strictly validating Content-Length and Transfer-Encoding, and ensuring Feathersjs parses the body exactly as the proxy and client expect. Below are concrete steps and code examples for Feathersjs services using Hmac Signatures.
1. Enforce consistent header handling and reject ambiguous encodings
Configure your Feathersjs app to reject requests with both Content-Length and Transfer-Encoding headers, as their coexistence can enable smuggling. Use an Express-compatible middleware (Feathersjs is built on Express) to normalize and validate headers before they reach your service logic.
// middleware/validate-headers.js
module.exports = function validateHeaders() {
return function(req, res, next) {
const hasTE = req.headers['transfer-encoding'];
const hasCL = req.headers['content-length'];
if (hasTE && hasCL) {
return res.status(400).send('Invalid headers: Content-Length and Transfer-Encoding must not both be present');
}
// Normalize: remove any whitespace and lowercase header names
if (hasTE) {
req.headers['transfer-encoding'] = hasTE.replace(/\s+/g, '').toLowerCase();
}
if (hasCL) {
req.headers['content-length'] = String(parseInt(req.headers['content-length'], 10));
}
next();
};
};
2. Canonicalize headers included in the Hmac signature
Ensure the client and server use the exact same set of headers and the same canonical format when computing the Hmac. Use a stable ordering and case-normalization (e.g., lowercase header names). Below is an example server-side Hmac verification that uses a canonical header string and rejects requests with unexpected headers.
// services/hmac-verify.js
const crypto = require('crypto');
const SECRET = process.env.HMAC_SECRET; // store securely
const ALLOWED_HEADERS = ['content-type', 'x-request-id', 'x-timestamp'];
function canonicalizeHeaders(headers) {
const normalized = {};
Object.keys(headers).forEach(k => {
const key = k.toLowerCase();
if (ALLOWED_HEADERS.includes(key)) {
normalized[key] = headers[k].trim();
}
});
// Deterministic ordering
const parts = ALLOWED_HEADERS.map(k => normalized[k] !== undefined ? `${k}:${normalized[k]}` : null).filter(Boolean);
return parts.join('\n');
}
function verifyHmac(req, res, next) {
const received = req.headers['x-hmac-signature'];
if (!received) {
return res.status(401).send('Missing Hmac signature');
}
const payload = canonicalizeHeaders(req.headers) + '\n' + req.body;
const expected = crypto.createHmac('sha256', SECRET).update(payload, 'utf8').digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(received, 'hex'), Buffer.from(expected, 'hex'))) {
return res.status(401).send('Invalid Hmac signature');
}
next();
}
module.exports = verifyHmac;
3. Apply middleware in the Feathersjs app pipeline
Insert the validation and verification middleware before body parsing and service hooks to ensure the body used for signature verification matches the body parsed by Feathersjs.
// app.js
const validateHeaders = require('./middleware/validate-headers');
const verifyHmac = require('./services/hmac-verify');
const app = require('feathers')();
const parser = require('body-parser');
app.use(parser.json({ limit: '1mb' }));
app.use(validateHeaders());
app.use(verifyHmac());
app.use('/users', require('./services/users'));
app.use('/accounts', require('./services/accounts'));
// Ensure hooks do not mutate the body in a way that breaks Hmac verification
app.hooks({
before: {
all: [],
find: [],
get: [],
create: ['validate-hmac-body'],
update: ['validate-hmac-body'],
patch: ['validate-hmac-body'],
remove: []
},
after: {
all: [],
find: [],
get: [],
create: [],
update: [],
patch: [],
remove: []
},
error: {
all: [],
find: [],
get: [],
create: [],
update: [],
patch: [],
remove: []
}
});
4. Reject requests with mismatched Content-Length
After body parsing, compare the received Content-Length (if present) with the actual byte length of the parsed body. Reject mismatches to prevent an attacker from smuggling by changing the declared length.
// middleware/content-length-check.js
module.exports = function contentLengthCheck() {
return function(req, res, next) {
const raw = req.headers['content-length'];
if (raw !== undefined) {
const expected = Buffer.byteLength(JSON.stringify(req.body), 'utf8');
if (parseInt(raw, 10) !== expected) {
return res.status(400).send('Content-Length mismatch');
}
}
next();
};
};
5. Use strict transport and header policies in production
In production, deploy Feathersjs behind a proxy that normalizes requests consistently and does not allow ambiguous encodings. Reject HTTP requests that switch from Content-Length to Transfer-Encoding mid-stream. Enforce HTTPS to prevent on-path modification of headers. These practices reduce the window for smuggling when Hmac Signatures are used.
middleBrick scans your Feathersjs endpoints and checks for header canonicalization issues and inconsistencies in how Content-Length and Transfer-Encoding are handled. Its findings include remediation guidance mapped to OWASP API Top 10 and relevant compliance frameworks, helping you address smuggling risks specific to Hmac Signatures without claiming to fix or block requests directly.