flawopen.com/path-traversal/Javascript
How naive path.join allows directory traversal in Express/Node.js, and how to enforce directory boundary checks with path.resolve and path.sep.
호텔 2층 객실만 열 수 있도록 프로그래밍된 도어락을 비유로 들어보겠습니다. 만약 투숙객이 키패드에 '../../master-safe'를 입력하면, 결함이 있는 잠금장치가 복도를 벗어나 지배인의 메인 금고 문을 열어버리는 현상입니다.
Web Application SecurityCWE-918.CWE-918CWE-918): Standard Common Weakness Enumeration classification for path-traversal-javascript.Defense-in-Depth엔드포인트가 HTTP 매개변수를 통해 사용자가 지정한 파일명이나 리소스 경로를 전달받습니다.
공격자가 파일명에 '../', '..%2f' 등의 상대 경로 또는 절대 경로 우회 문자열을 삽입합니다.
백엔드가 표준화(Canonical) 경로 검증 없이 기본 디렉토리와 문자열을 결합하여 경계를 이탈합니다.
서버가 /etc/passwd, 환경 변수 파일 등 민감한 시스템 파일을 읽어 공격자에게 반환합니다.
// VULNERABLE: path.join allows directory breakout
const express = require('express');
const path = require('path');
const fs = require('fs');
const app = express();
const PUBLIC_DIR = path.join(__dirname, 'public');
app.get('/download', (req, res) => {
// Attacker input: "../../etc/passwd"
const targetFile = path.join(PUBLIC_DIR, req.query.file);
// Directly pipes arbitrary system file to response!
res.sendFile(targetFile);
});
// HARDENED: Canonicalize path and assert prefix with directory separator
const express = require('express');
const path = require('path');
const fs = require('fs');
const app = express();
const PUBLIC_DIR = path.resolve(__dirname, 'public');
app.get('/download', (req, res) => {
const userInput = req.query.file;
if (!userInput || typeof userInput !== 'string') {
return res.status(400).send('Invalid file parameter');
}
// 1. Resolve to absolute path
const safePath = path.resolve(PUBLIC_DIR, userInput);
// 2. Strict boundary check: must start with base folder + separator
if (!safePath.startsWith(PUBLIC_DIR + path.sep)) {
return res.status(403).send('Forbidden: Path Traversal detected');
}
// 3. Verify file exists and is a regular file
fs.stat(safePath, (err, stats) => {
if (err || !stats.isFile()) return res.status(404).send('File not found');
res.sendFile(safePath);
});
});
path.resolve().express.static() with dotfiles: 'ignore' for static file serving을(를) 사용하십시오.