Request Smuggling in Feathersjs with Dynamodb
Request Smuggling in Feathersjs with Dynamodb — how this specific combination creates or exposes the vulnerability
Request smuggling occurs when an application processes HTTP requests differently depending on whether they are handled by the front-end proxy or the back-end application. In a Feathersjs application using DynamoDB as the data store, this typically relates to how HTTP headers, body parsing, and routing are handled before requests reach the Feathers service layer.
Feathersjs uses Express under the hood and relies on standard body-parsing middleware (e.g., express.json() and express.urlencoded()) and custom transports such as REST and Socket.io. If the request pipeline includes a reverse proxy or load balancer that parses and forwards requests differently than Feathersjs—such as handling Content-Length and Transfer-Encoding headers inconsistently—an attacker can craft requests that are interpreted in two ways: one by the proxy and another by Feathersjs.
When DynamoDB is used as the persistence layer, the risk does not stem from DynamoDB itself but from how requests are routed and authenticated before reaching the DynamoDB data access layer. For example, if a request is smuggled into a second route that bypasses authentication or rate limiting, the attacker may execute actions under a different identity or gain unauthorized access to DynamoDB-backed resources. Because Feathersjs services often rely on dynamic parameters to construct DynamoDB queries (e.g., using params.query to filter records), improperly validated requests can lead to unauthorized data access or injection-like behaviors even though DynamoDB does not execute arbitrary code.
Consider a Feathersjs service that expects a user ID in the URL path and performs ownership checks after retrieving parameters. If a request is split or duplicated due to smuggling, the front-end proxy might route one version to the intended user, while Feathersjs processes another with an altered path or header, potentially allowing access to another user’s DynamoDB record. This is a classic BOLA/IDOR pattern enabled by inconsistent request interpretation, not by DynamoDB behavior.
Additionally, because Feathersjs can serve both REST and real-time protocols, misconfigured transports may expose multiple entry points. If the REST layer is protected by one set of middleware rules while the Socket.io layer applies different parsing logic, an attacker may use smuggling to bypass intended controls. The DynamoDB layer remains agnostic to the transport, so any improperly validated input that reaches it could violate the principle of least privilege.
To detect this class of issue, scanning tools evaluate whether the API accepts and processes conflicting transfer encodings, whether chunked and content-length-based parsing is handled consistently, and whether route and header manipulation leads to different execution paths. These checks are part of the BOLA/IDOR and Input Validation categories in middleBrick’s 12 parallel security checks.
Dynamodb-Specific Remediation in Feathersjs — concrete code fixes
Remediation focuses on ensuring consistent request parsing, strict header handling, and explicit validation of all inputs that influence DynamoDB operations. Below are concrete examples for a Feathersjs service using the AWS SDK for JavaScript v3.
1. Explicit body parsing and header normalization
Ensure that body-parsing middleware is configured uniformly and that problematic headers are stripped or normalized before routing. For example:
const express = require('@feathersjs/express');
const app = express();
// Normalize headers to prevent smuggling via Transfer-Encoding
app.use((req, res, next) => {
if (req.headers['transfer-encoding']) {
// Reject or normalize chunked encoding if not expected
delete req.headers['transfer-encoding'];
}
next();
});
app.use(express.json({ limit: '10kb' }));
app.use(express.urlencoded({ extended: false }));
2. Strict validation of DynamoDB parameters
Always validate and sanitize inputs used in DynamoDB queries. Use a library such as ajv for schema validation before constructing requests.
const { DynamoDBClient, GetItemCommand } = require('@aws-sdk/client-dynamodb');
const Ajv = require('ajv');
const ajv = new Ajv();
const validateGetUser = ajv.compile({
type: 'object',
required: ['userId'],
properties: {
userId: { type: 'string', pattern: '^[a-zA-Z0-9_-]+$' }
}
});
class UserService {
constructor() {
this.dynamo = new DynamoDBClient({ region: 'us-east-1' });
}
async get(id, params) {
if (!validateGetUser(params)) {
const err = new Error('Invalid input');
err.code = 400;
throw err;
}
const command = new GetItemCommand({
TableName: process.env.USERS_TABLE,
Key: {
userId: { S: params.userId }
}
});
const response = await this.dynamo.send(command);
return response.Item ? { id: response.Item.userId.S } : null;
}
}
3. Enforce transport-specific security
Configure Feathers transports to reject ambiguous encodings and enforce strict content-length handling. For REST, disable unnecessary parsing options:
app.configure(rest({
strictContentNegotiation: true,
allowedHeaders: ['authorization', 'content-type'],
// Disable automatic parsing of certain headers that may be abused
onParse: (context) => {
if (context.request.headers['x-forwarded-proto'] &&
context.request.headers['x-forwarded-proto'] !== 'https') {
throw new Error('Invalid protocol');
}
return context;
}
}));
4. Consistent service-layer authorization
Ensure that every DynamoDB operation re-validates ownership or permissions, even if earlier middleware exists. Do not rely on route-level checks alone:
class SecureItemService {
async create(data, params) {
const userId = params.user ? params.user.userId : null;
if (!userId) {
throw new Error('Unauthorized');
}
const command = new PutItemCommand({
TableName: process.env.ITEMS_TABLE,
Item: {
userId: { S: userId },
data: { S: JSON.stringify(data) },
createdAt: { N: Date.now().toString() }
}
});
await this.dynamo.send(command);
return data;
}
}
5. Monitoring and testing
Use tools that test for request splitting and duplicate requests against your deployed service. Verify that responses are identical regardless of how the request is framed, and ensure that middleware does not inadvertently trust proxy headers without validation.