Что такое Path Traversal уязвимость и как её предотвратить?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Path Traversal уязвимость
Path Traversal (Directory Traversal) - это тип уязвимости, при которой злоумышленник может получить доступ к файлам и директориям, расположенным за пределами предполагаемой корневой директории веб-приложения. Атака использует недостаточную валидацию пользовательского ввода при формировании путей к файлам.
Как работает атака
Злоумышленник манипулирует параметрами запроса, используя специальные последовательности вроде ../ для выхода за пределы разрешённой директории:
// Уязвимый код
app.get("/files", (req, res) => {
const fileName = req.query.name;
const filePath = path.join(__dirname, "uploads", fileName);
res.sendFile(filePath); // опасно!
});
// Запрос: GET /files?name=../../../etc/passwd
// Результат: доступ к системному файлу
Основные векторы атаки
- Прямой обход:
../../etc/passwd - URL-кодирование:
%2e%2e%2fвместо../ - Двойное кодирование:
%252e%252e%252f - Null-byte инъекция:
../../etc/passwd%00.png(в старых версиях Node.js) - Unicode/UTF-8: использование символов типа
..%c0%af
Методы предотвращения
1. Нормализация и валидация пути:
import path from "path";
const UPLOADS_DIR = path.resolve(__dirname, "uploads");
app.get("/files", (req, res) => {
const fileName = req.query.name as string;
const resolvedPath = path.resolve(UPLOADS_DIR, fileName);
// Проверяем, что итоговый путь внутри разрешённой директории
if (!resolvedPath.startsWith(UPLOADS_DIR)) {
return res.status(403).json({ error: "Access denied" });
}
res.sendFile(resolvedPath);
});
2. Белый список файлов:
const allowedFiles = new Set(["report.pdf", "image.png", "data.csv"]);
app.get("/files", (req, res) => {
const fileName = path.basename(req.query.name as string);
if (!allowedFiles.has(fileName)) {
return res.status(404).json({ error: "File not found" });
}
res.sendFile(path.join(UPLOADS_DIR, fileName));
});
3. Использование path.basename для извлечения только имени файла без пути:
const safeName = path.basename(userInput); // "../../../etc/passwd" -> "passwd"
4. Дополнительные меры защиты:
- chroot или контейнеризация для ограничения файловой системы
- Запуск процесса с минимальными привилегиями
- Использование middleware вроде
helmetдля заголовков безопасности - Регулярные аудиты с инструментами типа Snyk, npm audit
- Запрет символических ссылок в директории загрузок (
lstatдля проверки)
Защита в популярных фреймворках
В Express.js статические файлы через express.static() уже защищены от path traversal. Но при ручной работе с fs.readFile или res.sendFile разработчик должен сам обеспечить валидацию.
В NestJS рекомендуется использовать StreamableFile и @Res() декоратор с предварительной проверкой пути через path.resolve + проверку startsWith.