# теория · шаг 1 из 5
alias, слеш и путь наружу
Коротко
Одна дыра, которую находят сканером за секунду и правят одним символом.
location /trav без слеша вместе с alias открывает соседние каталоги.
- Замер:
/trav../secrets/db.envвернул 200 и содержимое файла из каталога, которого в конфиге нет вовсе. - Со слешем (
location /trav/) тот же запрос даёт 404. - Нормализация URI тут не помогает:
..стоит внутри одного сегмента, схлопывать нечего. - Правило шире одной директивы: у
aliasслеши держат парой, как и уproxy_pass.
Если помнишь про alias без слеша и умеешь проверить его одной командой - листай до «Теперь сам».
Сначала ответь сам
location /trav {
alias /var/www/uploads/;
}
Рядом с uploads лежит каталог secrets с файлом db.env. Что вернёт запрос
/trav../secrets/db.env?
curl http://shop.local/trav../secrets/db.env
пароли
Файл. Целиком - curl печатает его тело. Код при этом 200 (виден через
-o /dev/null -w '%{http_code}'), а со слешем в location тот же запрос даёт
404 - разница в одном символе.
На что это похоже
Табличка на двери «склад» и правило: всё, что написано после слова «склад»,
дописывать к пути /дом/склад/. Посетитель пишет на бумажке «склад../сейф» -
получается /дом/склад/../сейф, то есть /дом/сейф.
Ошибка не в посетителе, а в том, что слово «склад» разрешили продолжать. Слеш в конце - это и есть точка, после которой продолжать нельзя.
Механизм: где склеивается путь
Нормализация URI из четвёртого раздела тут не спасает: она схлопывает /a/../b
в /b, а здесь точки стоят внутри одного сегмента - /trav.. - и
схлопывать нечего. Именно поэтому атака и записывается без слеша перед точками.
Правило
location /uploads/ { # слеш в location
alias /var/www/uploads/; # и слеш в alias
}
Слеши держат парой - то же правило, что у proxy_pass из седьмого раздела. Если
слеш в location не нужен по смыслу маршрута, вместо alias берут root:
location /uploads {
root /var/www; # даст /var/www/uploads/...
}
root складывает путь целиком и такой дыры не имеет вовсе: он не вырезает
совпавшую часть, а дописывает URI к корню.
Разбор: как проверить свой конфиг
curl -s -o /dev/null -w '%{http_code}\n' https://shop.local/uploads../etc/passwd
404 или 403 - хорошо. 200 - у тебя дыра, и чинится она одним символом.
Найти кандидатов в чужом конфиге можно за секунду:
nginx -T | grep -B2 alias | grep -E 'location [^/]*[^/]$'
Смотреть надо на все блоки с alias, у которых location не кончается слешем.
Их обычно один-два, и оба - наследие копирования.
Разбор: что ещё вытекает через alias
Вторая беда той же директивы - разъехавшиеся слеши без всякой атаки:
location /static/ {
alias /var/www/assets; # слеша нет
}
Запрос /static/app.js превращается в /var/www/assetsapp.js. В логе будет
честное open() "/var/www/assetsapp.js" failed (2: No such file or directory),
и по нему причина видна сразу - но только если в лог заглянуть.
Третья - alias внутри регулярного location. Там подстановка идёт по группам,
и без явного указания хвоста получается путь, который тебе не нужен:
location ~ ^/img/(.+)$ { # (.+) захватывает хвост пути в $1
alias /var/www/pictures/$1; # хвост подставляем сами
}
Без $1 такой блок отдал бы один и тот же файл на любой запрос.
Что ломается без этого
Дыра не абстрактная: curl со строкой /trav../ есть в любом сканере, и
проверяют её первым делом вместе с .git и .env из следующего урока.
Достаётся оттуда обычно не /etc/passwd (nginx работает не от root и его чаще
всего не прочитает), а гораздо более полезное для нападающего: конфиги
приложения этажом выше, .env с паролем к базе, бэкапы, ключи API.
Поэтому правильная реакция на находку - не только починить слеш, но и считать утёкшим всё, что лежало в соседних каталогах: пароли меняют, ключи перевыпускают.
Зачем это в работе
Ревью конфига на эту тему занимает минуту и делается по трём вопросам:
- Есть ли
aliasтам, гдеlocationбез слеша? - Есть ли
alias, у которого слеш только с одной стороны? - Можно ли вообще заменить
aliasнаroot?
Третий вопрос самый полезный. alias нужен, когда путь на диске не совпадает
с адресом; если совпадает - root проще и безопаснее. В большинстве конфигов
alias стоит просто по привычке.
Теперь сам
В конфиге location /files { alias /srv/files/; }. Коллега предлагает исправить
на location /files/ { alias /srv/files/; }, но замечает, что тогда сломается
адрес /files без слеша, по которому у них ходит старый клиент. Что делать?
Исправлять слеш обязательно, а старый адрес поддержать отдельной строкой:
location = /files { return 301 /files/; }. Точное совпадение обрабатывается
раньше префиксов, старый клиент получает редирект, дыра закрыта. Оставлять
location /files с alias ради совместимости нельзя - это ровно та
конфигурация, которую находят сканером.
Главное
location без слеша вместе с alias открывает соседние каталоги: замер даёт
200 и содержимое файла из /var/www/secrets/, хотя в конфиге этого каталога
нет. Нормализация URI не помогает - точки стоят внутри одного сегмента. Слеши у
alias держат парой, как у proxy_pass, а где можно - берут root, который
такой дыры не имеет вовсе. Проверяется одной командой curl с .. после имени
блока; найденную дыру чинят символом, но всё, что лежало рядом, считают утёкшим.
Комментарии
Пока нет комментариев. Будь первым!
Оставить комментарий