# теория · шаг 2 из 7
auth_basic и satisfy
Коротко
Второй рубеж после адреса - пароль. auth_basic даёт его одной строкой, без
приложения, без сессий и без базы.
- Клиент без пароля получает 401 и заголовок
WWW-Authenticate- браузер показывает системное окно. satisfy anyпревращает «адрес И пароль» в «адрес ИЛИ пароль»: из офиса без пароля, снаружи с паролем.auth_basic off;во вложенном блоке снимает с него проверку пароля - замер даёт 200 без единой попытки авторизации. Остальные ограничения (allow/deny,auth_request) при этом остаются.
Если знаешь про 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;
sudo htpasswd -c /etc/nginx/htpasswd vasya
htpasswd - утилита из пакета apache2-utils; ключ -c создаёт новый файл, без
него пользователь добавляется в существующий. После запуска команда спросит
пароль в терминале (ввод не виден) и запишет его хешем. Права на файл ставят
640, владельцем - root, группой - ту, под которой работают воркеры
(www-data в пакете Debian, nginx в образе). Для Debian:
sudo chown root:www-data /etc/nginx/htpasswd
sudo chmod 640 /etc/nginx/htpasswd
Для сборки с пользователем nginx замени группу www-data на 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 с
Верный пароль при этом не задерживается вовсе. Три секунды на попытку означают
двадцать попыток в минуту вместо тысяч - но только по одному соединению. Открыв
сто соединений разом, подбирающий получит сто ответов за те же три секунды:
задержка откладывает отказ, а не считает попытки. Поэтому auth_delay ставят
вместе с limit_req (частота) и limit_conn (одновременные соединения), а не
вместо них. Живой человек, ошибившийся паролем, разницы почти не заметит.
Что ломается без этого
Пароль ездит открытым текстом. 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: он не отвечает отказом сразу, а тратит время
атакующего на каждой неверной попытке. Лимит при этом оставь, и добавь
limit_conn: без них задержку обходят параллельными соединениями.
Главное
Basic Auth передаёт учётные данные в кодировке base64, поэтому для защиты пароля нужен TLS. Проверяй условие доступа целиком: satisfy any позволяет пройти по одному из способов. Отдельно проверь запрос извне без пароля и дочерние маршруты, где проверка могла быть отключена.
Комментарии
Пока нет комментариев. Будь первым!
Оставить комментарий