HIGH path traversalhapimongodb

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 IDNameSeverity
CWE-20Improper Input Validation HIGH
CWE-22Path Traversal HIGH
CWE-74Injection CRITICAL
CWE-77Command Injection CRITICAL
CWE-78OS Command Injection CRITICAL
CWE-79Cross-site Scripting (XSS) HIGH
CWE-89SQL Injection CRITICAL
CWE-90LDAP Injection HIGH
CWE-91XML Injection HIGH
CWE-94Code Injection CRITICAL

Frequently Asked Questions

How can I test if my Hapi routes are vulnerable to path traversal when using MongoDB?
Use unauthenticated scans with a tool like middleBrick which checks for path traversal indicators alongside MongoDB-related exposure. Additionally, manually test endpoints by sending path sequences such as ../../../ in parameters and verify that the application does not escape intended directories or reference unintended MongoDB collections.
Does validating ObjectId in Hapi fully prevent MongoDB-related traversal issues?
Validating ObjectId prevents malformed identifiers from reaching MongoDB, but path traversal risks also depend on how you use other user inputs (e.g., filenames, tenant identifiers). Always combine ObjectId validation with strict allowlists, path canonicalization, and avoid using untrusted input to derive database or collection names.