Path Traversal in Hapi with Api Keys
Path Traversal in Hapi with Api Keys — how this specific combination creates or exposes the vulnerability
Path Traversal occurs when user-controlled input is used to construct file system paths without proper validation, allowing an attacker to access files outside the intended directory. In a Hapi server that relies on API keys for access control, this risk can be amplified if keys are treated as a primary authorization boundary without validating file paths.
Consider a Hapi route that serves user-uploaded documents. If the route uses an API key to identify a tenant or user and directly concatenates a user-supplied filename into a filesystem path, an attacker can supply inputs like ../../../etc/passwd to traverse directories. Even with an API key in place, the server may still serve sensitive files if path normalization is missing. The API key identifies the caller but does not guarantee that the requested file belongs to the caller’s allowed scope, creating a privilege confusion scenario.
For example, a route defined as:
const Hapi = require('@hapi/hapi');
const fs = require('fs').promises;
const path = require('path');
const server = Hapi.server({ port: 4000 });
server.route({
method: 'GET',
path: '/files/{filename}',
options: {
validate: {
headers: Joi.object({
'api-key': Joi.string().required()
}).unknown()
},
handler: async (request, h) => {
const apiKey = request.headers['api-key'];
const tenantDir = `/data/${apiKey}`;
const filePath = path.join(tenantDir, request.params.filename);
return fs.readFile(filePath, 'utf8');
}
}
});
An attacker can send a request with api-key: tenantA and filename: ../../tenantB/config.json. The server resolves the path to /data/tenantA/../../tenantB/config.json, which may escape the tenant’s directory if the application does not canonicalize and verify that the resolved path remains within /data/tenantA. The presence of the API key does not prevent unauthorized file access; it only identifies the tenant making the request.
In this context, the API key is used for tenant identification but not for path scoping. Without additional checks—such as ensuring the resolved path starts with the expected base directory—an attacker can leverage directory traversal to read arbitrary files accessible to the process, including configuration files, logs, or other tenants’ data. This pattern is relevant to the BOLA/IDOR checks in middleBrick’s 12 security tests, which look for insecure direct object references and improper authorization boundaries even when authentication tokens like API keys are present.
Api Keys-Specific Remediation in Hapi — concrete code fixes
To mitigate path traversal when using API keys in Hapi, you must validate and sanitize file paths independently of the key-based tenant identification. The API key can determine the tenant directory, but the filename must be normalized and confined to that directory.
Use path.resolve and path.relative to ensure the resolved path does not escape the tenant root. Avoid directly concatenating user input into paths, and prefer using a strict allowlist for filenames or a mapping layer that decouples user input from filesystem names.
Secure example with proper path confinement:
const Hapi = require('@hapi/hapi');
const fs = require('fs').promises;
const path = require('path');
const server = Hapi.server({ port: 4000 });
server.route({
method: 'GET',
path: '/files/{filename}',
options: {
validate: {
headers: Joi.object({
'api-key': Joi.string().required()
}).unknown()
},
handler: async (request, h) => {
const apiKey = request.headers['api-key'];
const tenantDir = path.resolve('/data', apiKey);
const safeName = request.params.filename.replace(/[^a-zA-Z0-9._-]/g, '');
const filePath = path.resolve(tenantDir, safeName);
// Ensure the resolved path is within the tenant directory
if (!filePath.startsWith(tenantDir)) {
throw Boom.forbidden('Access denied');
}
return fs.readFile(filePath, 'utf8');
}
}
});
Key remediation steps:
- Resolve the tenant directory with
path.resolveto eliminate.and..segments. - Normalize the user-supplied filename by removing or replacing potentially dangerous characters, or map it to a storage-safe identifier.
- After constructing the full path, verify that it starts with the tenant directory; reject the request if it escapes.
- Consider using a filename-to-uuid mapping in your database so that user input never directly influences the filesystem path.
These practices complement API key usage by ensuring that even if an API key identifies a tenant, file access remains confined to that tenant’s directory, addressing both path traversal and authorization boundary concerns covered by middleBrick’s checks such as Property Authorization and BOLA/IDOR.
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 |