Path Traversal in Express with Dynamodb
Path Traversal in Express with Dynamodb — how this specific combination creates or exposes the vulnerability
Path Traversal occurs when an attacker manipulates a file or key path to access resources outside the intended directory or scope. In an Express API that uses DynamoDB as a backend, this typically surfaces through user-supplied parameters such as :filename, id, or key that are used to construct DynamoDB keys or query expressions without proper validation or escaping. Because DynamoDB stores data in a flat key-value model, logical path-like identifiers (e.g., tenantId/recordId or user/123/profile) can be abused if concatenated directly from request input, leading to unauthorized cross-partition or cross-object access.
An Express route that builds a DynamoDB KeyConditionExpression or a partition key from raw URL segments is vulnerable. For example, an endpoint like /users/:userId/profile may naïvely set Key = { PartitionKey: 'USER#' + req.params.userId }. If an attacker provides a crafted userId such as ../../../admin, and the application does not normalize or validate the parameter, the resulting key can traverse logical boundaries, potentially accessing other users’ data when the application’s key design uses hierarchical prefixes. DynamoDB itself does not interpret path separators, but the application logic that builds keys does; if those keys map to sensitive logical paths (e.g., ADMIN/audit/2024), data exposure occurs. This becomes critical when combined with insufficient authorization checks, effectively bypassing intended access controls.
Another scenario involves file-like references stored in DynamoDB, where object keys in an underlying storage layer (e.g., S3) are derived from user input. An Express endpoint that retrieves a document by id and uses that id to build an S3 key can allow path traversal if the id contains sequences like ../../secrets. Even when DynamoDB access is properly scoped, unsafe composition of storage paths leads to reading or overwriting unintended objects. The risk is amplified when the API exposes endpoints that accept multiple identifiers (e.g., /export/:tenantId/:reportName) without validating format or enforcing strict allowlists.
Middleware that performs naive string concatenation or uses deprecated path utilities can compound the issue. For instance, using path.join on user-controlled segments without canonicalization may not remove all traversal patterns when inputs contain encoded or mixed slashes. Since DynamoDB queries often rely on exact key matches, a malformed key can return no data in some cases, but in others, it may match a broader prefix than intended, returning multiple sensitive items. Therefore, the combination of Express routing, dynamic key construction, and DynamoDB’s partition key semantics creates a clear path for traversal when input validation and strict key design are omitted.
Dynamodb-Specific Remediation in Express — concrete code fixes
To mitigate Path Traversal in Express with DynamoDB, enforce strict input validation, canonicalize identifiers, and avoid direct concatenation of user input into DynamoDB keys or query expressions. Use allowlists for known-safe characters, enforce length limits, and normalize paths before using them as keys. Below are concrete, secure patterns for Express handlers.
Secure DynamoDB key construction
Always derive partition and sort keys from a controlled source, not raw user input. Use a mapping layer to translate validated identifiers to DynamoDB keys.
// Safe key construction in Express with DynamoDB DocumentClient
const { DynamoDBClient, GetCommand } = require('@aws-sdk/lib-dynamodb');
const client = new DynamoDBClient({ region: 'us-east-1' });
app.get('/users/:userId/profile', async (req, res) => {
const { userId } = req.params;
// Validate userId: alphanumeric + underscore, 3..36 chars
if (!/^[A-Za-z0-9_]{3,36}$/.test(userId)) {
return res.status(400).json({ error: 'Invalid user identifier' });
}
const command = new GetCommand({
TableName: process.env.USERS_TABLE,
Key: { PK: `USER#${userId}`, SK: 'PROFILE' },
});
try {
const { Item } = await client.send(command);
if (!Item) return res.status(404).json({ error: 'Not found' });
res.json(Item);
} catch (err) {
res.status(500).json({ error: 'Internal server error' });
}
});
Parameterized queries with condition expressions
Use ExpressionAttributeValues instead of string interpolation to avoid injection and traversal via crafted keys.
// Parameterized query to avoid path traversal in KeyConditionExpression
const { DynamoDBClient, QueryCommand } = require('@aws-sdk/lib-dynamodb');
const client = new DynamoDBClient({ region: 'us-east-1' });
app.get('/tenant/:tenantId/reports', async (req, res) => {
const { tenantId } = req.params;
if (!/^[A-Za-z0-9-]+$/.test(tenantId)) {
return res.status(400).json({ error: 'Invalid tenant identifier' });
}
const command = new QueryCommand({
TableName: process.env.REPORTS_TABLE,
KeyConditionExpression: 'PK = :pk',
ExpressionAttributeValues: {
':pk': `TENANT#${tenantId}`,
},
});
try {
const { Items } = await client.send(command);
res.json(Items || []);
} catch (err) {
res.status(500).json({ error: 'Internal server error' });
}
});
Safe file-like references for external storage
If your DynamoDB items reference objects in another storage system (e.g., S3), validate and scope the path before constructing storage keys.
// Validate and scope paths before using them to reference external objects
const path = require('path');
function safeStorageKey(base, userSupplied) {
const normalized = path.normalize(userSupplied).replace(/^(\/|\.\.\/)/, '');
return path.join(base, normalized);
}
app.get('/documents/:docId', async (req, res) => {
const { docId } = req.params;
if (!/^[A-Za-z0-9_-]+$/.test(docId)) {
return res.status(400).json({ error: 'Invalid document identifier' });
}
const storageKey = safeStorageKey('tenant-assets', docId);
// Use storageKey with S3 getObject, ensuring base directory containment
res.json({ storageKey });
});
General defenses
- Use allowlists for identifiers; reject any unexpected patterns.
- Do not trust path.join alone to prevent traversal—validate and canonicalize inputs explicitly.
- Scope DynamoDB access patterns to least privilege via partition key design, avoiding broad scans that could amplify traversal effects.
Related CWEs: inputValidation
| CWE ID | Name | Severity |
|---|---|---|
| CWE-20 | Improper Input Validation | HIGH |
| CWE-22 | Path Traversal | HIGH |
| CWE-74 | Injection | CRITICAL |
| CWE-77 | Command Injection | CRITICAL |
| CWE-78 | OS Command Injection | CRITICAL |
| CWE-79 | Cross-site Scripting (XSS) | HIGH |
| CWE-89 | SQL Injection | CRITICAL |
| CWE-90 | LDAP Injection | HIGH |
| CWE-91 | XML Injection | HIGH |
| CWE-94 | Code Injection | CRITICAL |