# теория · шаг 2 из 7
auth_basic и satisfy
Коротко
Второй рубеж после адреса - пароль. auth_basic даёт его одной строкой, без
приложения, без сессий и без базы.
- Клиент без пароля получает 401 и заголовок
WWW-Authenticate- браузер показывает системное окно. satisfy anyпревращает «адрес И пароль» в «адрес ИЛИ пароль»: из офиса без пароля, снаружи с паролем.auth_basic off;во вложенном блоке открывает его целиком - замер даёт 200 без единой попытки авторизации.auth_delay 3sзамедляет неверные попытки: замер - 3,004 с против 0,0008.
Если знаешь про satisfy any и помнишь, что basic-пароль ездит открытым текстом, - листай до «Теперь сам».
Сначала ответь сам
auth_basic "закрыто";
auth_basic_user_file /etc/nginx/htpasswd;
location /open/ { auth_basic off; }
Что вернёт /open/ без пароля?
curl -s -o /dev/null -w '%{http_code}\n' http://shop.local/open/
200
Двести, и это задумано: auth_basic off; - штатный способ открыть один
подкаталог внутри закрытого сайта. Опасен он тем, что выглядит безобидно и
пишется одной строкой - в чужом конфиге такую строку легко не заметить при
ревью.
На что это похоже
Домофон на подъезде. Он не знает, кто ты, - он знает код. Код один на всех, меняется редко, и любой, кому его назвали, входит как свой.
Этого хватает, чтобы отсечь случайных прохожих, и не хватает ни для чего
серьёзнее. auth_basic ровно такой: рубеж от сканеров и любопытных, а не
система учётных записей.
Механизм: два ответа и один заголовок
Правило
auth_basic "admin area"; # строка в кавычках - произвольный текст окна («realm»)
auth_basic_user_file /etc/nginx/htpasswd;
htpasswd -c /etc/nginx/htpasswd vasya
htpasswd - утилита из пакета apache2-utils; ключ -c создаёт новый файл, без
него пользователь добавляется в существующий. После запуска команда спросит
пароль в терминале (ввод не виден) и запишет его хешем. Права на файл ставят
640 (chmod 640 /etc/nginx/htpasswd), владельцем - пользователя, под которым
работают воркеры (www-data в пакете, nginx в образе), чтобы файл читал
nginx, но не посторонние.
Замер поведения:
| запрос | ответ |
|---|---|
| без пароля | 401 |
| с верным паролем | 200 |
в блоке с auth_basic off |
200 |
Разбор: satisfy any
satisfy any;
allow 192.168.1.0/24;
deny all;
auth_basic "admin";
auth_basic_user_file /etc/nginx/htpasswd;
По умолчанию действует satisfy all - выполнены должны быть оба условия: и
адрес подошёл, и пароль верный. С satisfy any достаточно одного:
200
Из офиса пускает молча, из дома спрашивает пароль. Это самая ходовая схема для
внутренних панелей, и она же объясняет, зачем в предыдущем уроке allow без
пароля был назван первым рубежом, а не единственным.
Обратная ошибка тоже встречается: satisfy all (умолчание) вместе с allow из
офиса и паролем означает, что из дома не пустят даже с верным паролем - адрес не
подошёл.
Разбор: auth_delay против перебора
Пароль один, и его будут подбирать. limit_req из прошлой главы отсекает
частоту, но у него есть неприятная сторона: он отвечает 503 или 429, и по этому
ответу подбирающий понимает, что нашёл живую точку. auth_delay работает иначе -
он просто задерживает неверные попытки:
auth_delay 3s;
без auth_delay: 0,0008 с
с auth_delay 3s: 3,004 с
Верный пароль при этом не задерживается вовсе. Три секунды на попытку означают двадцать попыток в минуту вместо тысяч - перебор по словарю становится бессмысленным, а живой человек, ошибившийся паролем, разницы почти не заметит.
Что ломается без этого
Пароль ездит открытым текстом. Basic - это base64 от строки
логин:пароль, обратимое кодирование. По http его читает любой на пути;
поэтому auth_basic без TLS не имеет смысла вовсе, а с TLS - имеет ровно
столько, сколько стоит один общий пароль.
Пароль общий и не отзывается. Уволился человек - меняешь пароль всем.
Никакого «кто именно зашёл» в логах нет (точнее, есть $remote_user, но он
показывает только имя из файла, а имён обычно одно-два). Для внутренней панели
это приемлемо, для системы с ролями - нет.
Забытый auth_basic off. Строка в одном вложенном блоке открывает его
целиком, и заметить её при беглом чтении конфига трудно. Проверять надо
запросом, а не глазами:
curl -s -o /dev/null -w '%{http_code}\n' https://shop.local/admin/static/app.js
Зачем это в работе
auth_basic уместен там, где нужно быстро закрыть что-то от интернета и не
строить систему: stub_status, панель метрик, тестовый стенд, страница
разработчика. Всё, где пользователей больше двух и важно знать, кто вошёл,
делают приложением - или auth_request из следующего урока, который отдаёт
решение внешнему сервису.
Одна практическая деталь: auth_basic действует и на подзапросы -
внутренние запросы, которые nginx делает сам (например, для auth_request или
error_page). Если
внутри закрытого паролем location стоит proxy_pass на бэкенд, который сам
ходит за чем-то через nginx, эти внутренние запросы тоже упрутся в пароль.
Лечится auth_basic off; в служебном блоке - осознанно и с комментарием.
Теперь сам
Панель закрыли auth_basic и limit_req zone=login burst=3 nodelay;. Через
месяц в логе видно тысячи 401 с одного адреса в час. Лимит работает, а перебор
идёт. Как так?
limit_req с burst=3 nodelay пропускает пачку, а дальше отдаёт 429 или 503 -
но подбирающему это не мешает: он просто держит темп чуть ниже порога и
продолжает. Тысяча попыток в час укладывается в один запрос каждые четыре
секунды. Добавь auth_delay 3s: он не отвечает отказом, а тратит время
атакующего на каждой неверной попытке, и темп падает принудительно.
Главное
auth_basic - один общий пароль без сессий и учёта, рубеж от сканеров и
любопытных. Без пароля клиент получает 401 с WWW-Authenticate, а сам пароль
едет в base64, то есть без TLS не защищён вовсе. satisfy any превращает «адрес
и пароль» в «адрес или пароль» - типовая схема для внутренних панелей.
auth_basic off; в дочернем блоке открывает его целиком, и проверять это надо
запросом. Перебор тормозят не лимитом, а auth_delay: замер даёт 3 секунды на
неверную попытку против тысячной доли секунды.
Комментарии
Пока нет комментариев. Будь первым!
Оставить комментарий