JAVASCRIPT
Prevent Directory Traversal Attacks in Node.js File Access
Secure your Node.js application from directory traversal vulnerabilities by sanitizing user-controlled file paths before accessing the file system, preventing unauthorized file access.
const express = require('express');
const path = require('path');
const fs = require('fs');
const app = express();
// Define a safe base directory where files can be accessed
const BASE_DIR = path.resolve(__dirname, 'data');
// Ensure the base directory exists
if (!fs.existsSync(BASE_DIR)) {
fs.mkdirSync(BASE_DIR);
fs.writeFileSync(path.join(BASE_DIR, 'secret.txt'), 'This is a secret file.');
fs.writeFileSync(path.join(BASE_DIR, 'public_report.pdf'), 'Public report content.');
}
app.get('/download/:filename', (req, res) => {
const userRequestedFilename = req.params.filename;
// --- SECURE WAY: Resolve path and ensure it's within BASE_DIR ---
const filePath = path.join(BASE_DIR, userRequestedFilename);
const resolvedPath = path.resolve(filePath);
// Crucial security check: Ensure the resolved path starts with the BASE_DIR
if (!resolvedPath.startsWith(BASE_DIR + path.sep)) {
// Malicious attempt to access files outside BASE_DIR
console.warn(`Path traversal attempt detected: ${userRequestedFilename}`);
return res.status(403).send('Access Denied: Invalid file path.');
}
fs.readFile(resolvedPath, (err, data) => {
if (err) {
console.error(`Error reading file ${resolvedPath}:`, err);
// Be generic to avoid leaking file system structure
return res.status(404).send('File not found or inaccessible.');
}
res.type(path.extname(resolvedPath)); // Set appropriate Content-Type
res.send(data);
});
});
// Example of insecure approach (DO NOT USE IN PRODUCTION)
app.get('/download_insecure/:filename', (req, res) => {
const insecurePath = path.join(__dirname, req.params.filename); // Allows ../../..
// This is highly vulnerable to path traversal
// e.g., /download_insecure/../../../../etc/passwd
fs.readFile(insecurePath, (err, data) => {
if (err) return res.status(404).send('File not found (insecure example).');
res.send(data);
});
});
app.listen(3000, () => {
console.log('Server running on http://localhost:3000');
console.log('Try: http://localhost:3000/download/secret.txt');
console.log('Try to traverse: http://localhost:3000/download/../secret.txt or /download/../../../../etc/passwd');
});
How it works: This Node.js snippet demonstrates how to prevent directory traversal (also known as path traversal) attacks. It establishes a `BASE_DIR` as the only allowed directory for file access. Before reading any file requested by a user, it uses `path.join` and `path.resolve` to construct the full path and then critically checks if the `resolvedPath` still starts with the `BASE_DIR`. This ensures that malicious inputs like `../../etc/passwd` are detected and blocked, preventing attackers from accessing files outside the intended directory.