Path Traversal in Hapi with Dynamodb
Path Traversal in Hapi with Dynamodb — how this specific combination creates or exposes the vulnerability
Path Traversal in a Hapi application that uses DynamoDB typically arises when user-controlled path segments are used to construct keys or query parameters without strict validation or canonicalization. Hapi routes often pass request parameters (e.g., params.id or query strings) into data access logic. If these values are concatenated into a DynamoDB key condition or used to build file-like identifiers (e.g., folder-based key names), an attacker can supply sequences like ../../../sensitive to escape intended namespace boundaries.
DynamoDB itself does not have a filesystem path concept, but path traversal manifests here through key design and access patterns. For example, a poorly designed partition key might embed a path hierarchy (e.g., tenantId/locale/resourceId). If an Hapi route builds a KeyConditionExpression using raw user input without sanitization, an attacker can manipulate the key traversal to target items outside their tenant or intended scope. This can lead to horizontal IDOR when multi-tenant data is stored under shared table prefixes and access checks are incomplete.
Consider an Hapi route that retrieves a user profile using a path-like identifier:
// Unsafe: directly using user input to build DynamoDB KeyConditionExpression
const key = `${tenantId}/${userId}`;
const params = {
TableName: 'UserProfiles',
KeyConditionExpression: 'pk = :pk',
ExpressionAttributeValues: { ':pk': key },
};
If userId comes from an unvalidated parameter and includes path traversal sequences, an attacker can craft a userId such as ../../../admin/123, causing the partition key to resolve to a different tenant’s data. Even when using DynamoDB’s native key structure, logical path traversal occurs when authorization checks are performed after key construction, allowing an attacker to read or modify items they should not access.
Another scenario involves file-like object references stored in DynamoDB where the primary key includes a path derived from user input. If the Hapi service uses this key to later generate pre-signed URLs or invoke other services, traversal in the logical path can lead to unauthorized access across logical boundaries. While DynamoDB does not interpret path separators, the application’s mapping from logical paths to keys must be validated to prevent privilege escalation or data exposure.
Dynamodb-Specific Remediation in Hapi — concrete code fixes
To prevent path traversal in Hapi when working with DynamoDB, validate and sanitize all user input used to construct keys, KeyConditionExpression, or attribute values. Use allowlists for known identifiers and enforce strict character rules. Avoid concatenating raw user input into key strings; instead, use parameter binding and structured access patterns.
Below is a secure Hapi route example that validates and uses a numeric user ID to build a DynamoDB query:
const Hapi = require('@hapi/hapi');
const { DynamoDBClient, QueryCommand } = require('@aws-sdk/client-dynamodb');
const client = new DynamoDBClient({ region: 'us-east-1' });
const validateUserId = (value) => {
// Allow only digits, ensuring no path traversal or injection
return /^[0-9]+$/.test(value);
};
const server = Hapi.server({ port: 4000, host: 'localhost' });
server.route({
method: 'GET',
path: '/profiles/{tenantId}/{userId}',
handler: async (request) => {
const { tenantId, userId } = request.params;
// Validate tenantId and userId to prevent path traversal
if (!/^[a-zA-Z0-9_-]{1,64}$/.test(tenantId)) {
throw Boom.badRequest('Invalid tenant identifier');
}
if (!validateUserId(userId)) {
throw Boom.badRequest('Invalid user identifier');
}
const params = {
TableName: 'UserProfiles',
KeyConditionExpression: 'pk = :pk AND begins_with(sk, :sk)',
ExpressionAttributeValues: {
':pk': { S: `tenant#${tenantId}` },
':sk': { S: `user#${userId}` },
},
};
try {
const command = new QueryCommand(params);
const response = await client.send(command);
return response.Items || [];
} catch (error) {
console.error('DynamoDB query error:', error);
throw Boom.internal('Unable to retrieve profiles');
}
},
});
(async () => {
await server.start();
console.log('Server running on %s', server.info.uri);
})();
In this example, tenantId and userId are validated before being used in DynamoDB attribute values. The partition key is built using a controlled prefix (tenant#) and the validated tenantId, while the sort key uses a validated userId. This prevents path-like traversal by disallowing characters that could change namespace interpretation.
For broader protection, apply middleware in Hapi to normalize and reject unexpected input patterns, and ensure authorization logic re-validates tenant context on every request. Use DynamoDB’s native data types safely and avoid building key strings from concatenated user-controlled segments without strict checks.
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 |