HIGH path traversalexpressbasic auth

Path Traversal in Express with Basic Auth

Path Traversal in Express with Basic Auth — how this specific combination creates or exposes the vulnerability

Path Traversal in Express when Basic Authentication is used can expose protected resources or allow an attacker to read files outside the intended directory. The vulnerability occurs when user-supplied path input is concatenated with a base directory without strict validation or normalization, enabling sequences like ../ to traverse directory boundaries. Basic Auth in this context provides only identity verification, not authorization or input safety, so a compromised or low-privilege account can still leverage path manipulation to reach sensitive locations such as configuration files, application source code, or credential stores.

Consider an endpoint that serves user-uploaded or system files without ensuring the resolved path remains within an allowed directory:

const express = require('express');
const fs = require('fs');
const path = require('path');

const app = express();
const BASE_DIR = '/var/app/private';

app.get('/files', (req, res) => {
  const filename = req.query.name;
  const filePath = path.join(BASE_DIR, filename);
  fs.readFile(filePath, (err, data) => {
    if (err) return res.status(400).send('File not found');
    res.send(data);
  });
});

app.listen(3000);

If filename is supplied as ../../../etc/passwd, path.join may still produce a path that escapes /var/app/private depending on how the runtime resolves segments. An attacker authenticated with Basic Auth can repeatedly probe endpoints to discover accessible parent directories, and because Basic Auth does not enforce least-privilege file access, the impact is elevated: attackers can read arbitrary files under the same runtime identity. This combination illustrates why authentication and strict path controls must be addressed independently.

Another common pattern is using headers like Authorization to derive a user-specific base directory (e.g., per-tenant storage). If the tenant identifier is used directly in path construction without canonicalization, an attacker can traverse across tenant boundaries or into system paths. MiddleBrick’s checks for Path Traversal in such flows include validating that resolved paths remain within an allowed prefix and rejecting inputs containing encoded slashes, dot segments, or absolute paths. Even with Basic Auth enforcing initial access, the scan will flag missing path constraints as a high-severity finding because the control boundary between authenticated identity and safe file access is missing.

Basic Auth-Specific Remediation in Express — concrete code fixes

Remediation focuses on ensuring that authenticated access does not imply safe path resolution. Always treat user input as untrusted, regardless of the presence of Basic Auth. Use strict allowlists, canonical resolution, and avoid dynamic path concatenation when possible. The following examples show secure patterns for Express with Basic Auth.

1. Validate and sanitize input before path operations

Normalize and validate the filename, and ensure the resolved path remains within the intended directory.

const express = require('express');
const fs = require('fs');
const path = require('path');

const app = express();
const BASE_DIR = path.resolve('/var/app/private');

app.get('/files', (req, res) => {
  const filename = req.query.name;
  if (typeof filename !== 'string') {
    return res.status(400).send('Invalid filename');
  }
  // Remove any null bytes and reject dangerous characters
  if (/[^a-zA-Z0-9._-]/.test(filename)) {
    return res.status(400).send('Invalid filename');
  }
  const filePath = path.resolve(BASE_DIR, filename);
  if (!filePath.startsWith(BASE_DIR)) {
    return res.status(403).send('Access denied');
  }
  fs.readFile(filePath, (err, data) => {
    if (err) return res.status(404).send('File not found');
    res.type(path.extname(filePath)).send(data);
  });
});

app.listen(3000);

2. Use a strict allowlist for accessible files or directories

Instead of computing paths from user input, map allowed files or use a lookup table to avoid traversal entirely.

const express = require('express');
const fs = require('fs');
const path = require('path');

const app = express();
const BASE_DIR = path.resolve('/var/app/private');
const ALLOWED = new Set(['report.csv', 'config.json', 'readme.txt']);

app.get('/files', (req, res) => {
  const filename = req.query.name;
  if (!ALLOWED.has(filename)) {
    return res.status(403).send('Forbidden');
  }
  const filePath = path.join(BASE_DIR, filename);
  fs.readFile(filePath, (err, data) => {
    if (err) return res.status(404).send('File not found');
    res.type(path.extname(filePath)).send(data);
  });
});

app.listen(3000);

3. Middleware for Basic Auth with path-aware checks

Combine standard Basic Auth with path validation to ensure authenticated requests are also constrained.

const express = require('express');
const auth = require('basic-auth');
const fs = require('fs');
const path = require('path');

const app = express();
const BASE_DIR = path.resolve('/var/app/private');

const users = {
  alice: process.env.ALICE_PASS,
  bob: process.env.BOB_PASS,
};

function requireAuth(req, res, next) {
  const user = auth(req);
  if (!user || !users[user.name] || users[user.name] !== user.pass) {
    res.set('WWW-Authenticate', 'Basic realm="Files"');
    return res.status(401).send('Authentication required');
  }
  next();
}

app.get('/files', requireAuth, (req, res) => {
  const filename = req.query.name;
  if (typeof filename !== 'string' || !/^[
a-zA-Z0-9._-]+$/.test(filename)) {
    return res.status(400).send('Invalid filename');
  }
  const filePath = path.resolve(BASE_DIR, filename);
  if (!filePath.startsWith(BASE_DIR)) {
    return res.status(403).send('Access denied');
  }
  fs.readFile(filePath, (err, data) => {
    if (err) return res.status(404).send('File not found');
    res.type(path.extname(filePath)).send(data);
  });
});

app.listen(3000);

These examples demonstrate how to integrate path validation with authentication, ensuring that Basic Auth controls identity while additional checks control safe access to the filesystem.

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

Does Basic Auth alone prevent path traversal in Express APIs?
No. Basic Auth only verifies identity; it does not validate or sanitize file paths. Without strict path constraints, authenticated users can still traverse directories using crafted inputs such as ../../../etc/passwd. Always combine authentication with allowlist validation and canonical path resolution.
What input validation is sufficient to prevent path traversal in Express file endpoints?
Use an allowlist of known-safe filenames or IDs, resolve paths with path.resolve, and assert that the resolved path starts with the intended base directory. Reject null bytes, dot segments, and absolute paths, and avoid directly concatenating user input into filesystem paths.