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 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
Does Basic Auth alone prevent path traversal in Express APIs?
../../../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?
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.