Path Traversal in Hapi with Mongodb
Path Traversal in Hapi with Mongodb — how this specific combination creates or exposes the vulnerability
Path Traversal in a Hapi application using MongoDB typically arises when user-controlled path input is used to construct filesystem operations or dynamic query selectors without proper validation. An attacker can supply sequences like ../../../etc/passwd or crafted ObjectId/field values to escape intended directory scopes or to traverse logical document boundaries. In Hapi, routes that directly interpolate request parameters into filesystem calls or into MongoDB query filters can expose directory traversal or unintended data access.
For example, a route that builds a file path from a user-supplied folder and file name concatenates values without canonicalization can be abused to traverse parent directories. If the same route also references MongoDB by using parts of the path to select a database or collection, an attacker may leverage traversal patterns to probe or reference unintended documents. Even when input does not directly reach MongoDB, logging or error messages that include the traversed path may reveal internal structure that assists further attacks.
Consider a route that intends to read a user’s profile picture stored under a user-controlled subfolder. If the implementation does not normalize and validate the folder and file names, an input such as ../../users/admin/profile.jpg can escape the intended base directory. If the route additionally uses a portion of the path to decide which MongoDB collection to query (e.g., using a tenant identifier derived from the path), traversal can lead to reading or writing data in collections outside the tenant’s scope, effectively a form of Insecure Direct Object Reference (IDOR) facilitated by path manipulation.
Another scenario involves accepting an ObjectId from the client and using it both to locate a document in MongoDB and to derive a storage path. If the ObjectId is concatenated with user input to form a filesystem path without strict validation, attackers may attempt to inject path segments that traverse outside the document’s expected storage location. While MongoDB itself does not perform filesystem traversal, the application logic that maps user input to both database selection and filesystem navigation creates a linkage where a traversal bug in path handling can lead to unauthorized data exposure or manipulation in MongoDB.
Hapi does not inherently protect against these risks; developers must enforce strict allowlists, canonicalize paths, and avoid using untrusted input to derive database identifiers or collection names. Security checks such as those performed by middleBrick can identify path traversal indicators alongside MongoDB-related exposure in unauthenticated scans, highlighting risky parameter handling and improper input validation within the API surface.
Mongodb-Specific Remediation in Hapi — concrete code fixes
To mitigate path traversal when Hapi interacts with MongoDB, validate and sanitize all user input used in paths and database selectors. Use a strict allowlist for filenames and directory segments, canonicalize paths with a trusted utility, and avoid dynamic database or collection names derived from user input. Below are concrete code examples for a Hapi route that reads a file and fetches a related MongoDB document safely.
First, define a route that accepts an identifier and a filename, validates the filename against an allowlist, resolves the path securely, and uses a trusted ObjectId to query MongoDB:
const Hapi = require('@hapi/hapi');
const path = require('path');
const fs = require('fs').promises;
const { ObjectId } = require('mongodb');
const allowedExtensions = new Set(['jpg', 'jpeg', 'png', 'gif']);
async function validateFilename(input) {
const ext = path.extname(input).toLowerCase().slice(1);
const basename = path.basename(input);
// Prevent path traversal sequences and null bytes
if (basename.includes('..') || basename.includes('\\') || input.includes('\0')) {
throw new Error('Invalid filename');
}
if (!allowedExtensions.has(ext)) {
throw new Error('Unsupported file extension');
}
return basename;
}
async function getProfilePicture(request, h) {
const { userId, filename } = request.params;
// Validate userId as a valid ObjectId
if (!ObjectId.isValid(userId)) {
return h.response({ error: 'Invalid user identifier' }).code(400);
}
const objectId = new ObjectId(userId);
// Validate and sanitize filename
const safeName = await validateFilename(filename);
// Use a fixed base directory, no user-controlled path segments
const baseDir = '/var/app/public/profile-pictures';
const filePath = path.join(baseDir, safeName);
// Ensure resolved path is within base directory
const resolved = path.resolve(filePath);
if (!resolved.startsWith(path.resolve(baseDir))) {
throw new Error('Path traversal attempt detected');
}
const db = request.server.app.db; // Assume MongoDB client attached to server
const user = await db.collection('users').findOne({ _id: objectId }, { projection: { username: 1 } });
if (!user) {
return h.response({ error: 'User not found' }).code(404);
}
try {
const data = await fs.readFile(resolved);
return data; // Return file buffer or use reply.file in production
} catch (err) {
request.log(['error', 'file'], err);
return h.response({ error: 'Unable to read file' }).code(500);
}
}
const server = Hapi.server({ port: 4000 });
server.route({
method: 'GET',
path: '/profile/{userId}/{filename}',
handler: getProfilePicture
});
(async () => {
await server.start();
// Attach MongoDB client to server.app in real setup
})();
Key remediation points illustrated:
- Validate ObjectId before using it in a MongoDB query to avoid injection or malformed identifiers.
- Use a strict allowlist for file extensions and reject paths containing
..or backslashes. - Canonicalize and check that the resolved file path remains within the intended base directory.
- Avoid using any user-controlled value to select a MongoDB database or collection; keep those identifiers fixed or map them through a server-side configuration.
For applications that must support multiple tenants, map tenant identifiers server-side (e.g., via a lookup table) rather than concatenating them into paths or collection names. This approach prevents traversal across logical boundaries and reduces the risk of IDOR linked to path manipulation.
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 |
Frequently Asked Questions
How can I test if my Hapi routes are vulnerable to path traversal when using MongoDB?
../../../ in parameters and verify that the application does not escape intended directories or reference unintended MongoDB collections.