Request Smuggling in Express with Dynamodb
Request Smuggling in Express with Dynamodb — how this specific combination creates or exposes the vulnerability
Request smuggling occurs when an Express application processes HTTP requests differently between a frontend proxy/load balancer and the Express server, allowing attackers to smuggle requests across security boundaries. When Express interacts with DynamoDB, the risk amplifies if request parsing inconsistencies exist between the proxy and the Node.js runtime, or when DynamoDB operations are conditioned on attacker-controlled headers that are inadvertently trusted.
In a typical setup, an API Gateway or Nginx terminates TLS and forwards requests to Express. If the proxy normalizes or drops headers (e.g., Transfer-Encoding or Content-Length) differently than Express, a smuggled request can reach DynamoDB with an unintended method or path. For example, a request like POST /api/users HTTP/1.1\r\nContent-Length: 13\r\nTransfer-Encoding: chunked\r\n...\r\n0\r\n\r\nDELETE /api/users/123 may be parsed by the proxy as a safe POST, while Express interprets the smuggled DELETE due to incomplete header sanitization. If the Express route directly constructs a DynamoDB PutItem or UpdateItem using the request body without strict validation, the smuggled method can execute unintended DynamoDB operations, bypassing intended access controls.
DynamoDB-specific exposure arises when Express routes use unvalidated input to build Key conditions, UpdateExpression, or ConditionExpression. A smuggled parameter such as user_id could change the target item in a DynamoDB operation, leading to IDOR or privilege escalation. For instance, an Express route that merges req.params.id into a DynamoDB key without verifying it matches the authenticated user’s ID allows a smuggled request to target arbitrary items. Additionally, if the route uses DynamoDB’s ReturnValues or ProjectionExpression based on client-supplied headers, smuggled inputs can leak sensitive attributes or trigger excessive operations.
Because DynamoDB is a managed NoSQL service, the impact includes unauthorized data modification, reads of other users’ records, or injection of malformed attribute values that could disrupt downstream processing. The combination of header-parsing ambiguities in the proxy/Express boundary and dynamic construction of DynamoDB commands creates a path where a single smuggled request can compromise data integrity and confidentiality across user boundaries.
Dynamodb-Specific Remediation in Express — concrete code fixes
Remediation focuses on strict header handling, input validation, and isolating DynamoDB command construction from untrusted sources. Use a reverse proxy that normalizes Transfer-Encoding and Content-Length consistently, and configure Express to reject requests with both headers. Validate and canonicalize all inputs before referencing them in DynamoDB operations.
Express route with safe DynamoDB usage
const express = require('express');
const { DynamoDBClient, GetItemCommand, UpdateItemCommand } = require('@aws-sdk/client-dynamodb');
const app = express();
app.use(express.json({
strict: true, // reject arrays and primitives
type: 'application/json'
}));
// Reject requests with both Content-Length and Transfer-Encoding
app.use((req, res, next) => {
const hasTE = req.headers['transfer-encoding'];
const hasCL = req.headers['content-length'];
if (hasTE && hasCL) {
return res.status(400).json({ error: 'Invalid headers: Content-Length and Transfer-Encoding cannot both be present' });
}
next();
});
app.put('/api/users/:userId', async (req, res) => {
const client = new DynamoDBClient({ region: 'us-east-1' });
const { userId } = req.params;
const { attribute, value } = req.body;
// Validate userId format to prevent injection
if (!/^[a-f0-9-]{36}$/.test(userId)) {
return res.status(400).json({ error: 'Invalid user ID' });
}
// Validate attribute against a denylist/allowlist
const allowedAttributes = ['email', 'preferences', 'status'];
if (!allowedAttributes.includes(attribute)) {
return res.status(400).json({ error: 'Invalid attribute' });
}
// Use explicit key and condition to avoid accidental overwrites
const params = {
TableName: 'Users',
Key: {
user_id: { S: userId },
},
UpdateExpression: 'SET #attr = :val',
ConditionExpression: 'attribute_exists(user_id)',
ExpressionAttributeNames: { '#attr': attribute },
ExpressionAttributeValues: {
':val': { S: String(value) },
},
ReturnValues: 'NONE',
};
try {
const command = new UpdateItemCommand(params);
await client.send(command);
res.json({ status: 'updated' });
} catch (err) {
if (err.name === 'ConditionalCheckFailedException') {
return res.status(409).json({ error: 'Precondition failed' });
}
res.status(500).json({ error: 'Internal server error' });
}
});
app.get('/api/users/:userId', async (req, res) => {
const client = new DynamoDBClient({ region: 'us-east-1' });
const { userId } = req.params;
if (!/^[a-f0-9-]{36}$/.test(userId)) {
return res.status(400).json({ error: 'Invalid user ID' });
}
const params = {
TableName: 'Users',
Key: {
user_id: { S: userId },
},
ProjectionExpression: 'user_id, email, status',
};
try {
const command = new GetItemCommand(params);
const data = await client.send(command);
if (!data.Item) {
return res.status(404).json({ error: 'Not found' });
}
res.json({
user_id: data.Item.user_id.S,
email: data.Item.email.S,
status: data.Item.status.S,
});
} catch (err) {
res.status(500).json({ error: 'Internal server error' });
}
});
module.exports = app;
Key practices:
- Enforce strict header parsing to eliminate ambiguity between proxy and Express.
- Validate and sanitize all inputs used in DynamoDB command construction; never directly concatenate user input into
Key,UpdateExpression, orConditionExpression. - Use ConditionExpression to ensure intended item state and avoid overwrites caused by smuggled parameters.
- Apply least-privilege IAM roles to the Express service so that a compromised DynamoDB operation is constrained.
Frequently Asked Questions
How does middleBrick detect request smuggling risks involving DynamoDB in Express?
Can the middleBrick CLI scan an Express API that uses DynamoDB and enforce a score threshold in CI?
middlebrick scan <url>, and integrate the GitHub Action to fail builds if the security score drops below your chosen threshold, helping prevent deployments with smuggling risks.