TL;DR
Path traversal (or directory traversal) happens when user input is used to construct file paths without proper validation. Attackers use ../ sequences to escape the intended directory and read sensitive files like /etc/passwd or your .env file. Always resolve paths and verify they stay within the allowed directory.
How Path Traversal Works
// User requests: /api/files?name=../../../etc/passwd
app.get('/api/files', (req, res) => {
const filename = req.query.name;
const filepath = path.join('/uploads', filename); // VULNERABLE!
// This reads /etc/passwd, not a file in /uploads
res.sendFile(filepath);
});
Common targets: /etc/passwd, .env, config files, source code, private keys. On Windows: C:\Windows\System32\config\SAM, web.config
Bypass Attempts
Simple blacklisting of "../" is not enough. Attackers use encodings:
..%2f(URL encoded)..%252f(double encoded)....//(after stripping ../)..\(Windows paths)
How to Prevent Path Traversal
import path from 'path';
const UPLOAD_DIR = '/var/app/uploads';
app.get('/api/files', (req, res) => {
const filename = req.query.name;
// Resolve to absolute path
const requestedPath = path.resolve(UPLOAD_DIR, filename);
// Verify it's still within the allowed directory
if (!requestedPath.startsWith(UPLOAD_DIR + path.sep)) {
return res.status(403).json({ error: 'Access denied' });
}
// Now safe to use
res.sendFile(requestedPath);
});
Is path.join() safe?
No. path.join() just combines paths, it doesn't prevent traversal. You must use path.resolve() and then verify the result is within your allowed directory.
What about symbolic links?
Symlinks can also be used for traversal. Use fs.realpath() to resolve symlinks before checking the path, or disable symlinks in your upload directory.
Scan for Path Traversal
Our scanner tests file endpoints for path traversal vulnerabilities.
Start Free Scan