Приветствую!
У меня настроены map {} фильтры на поиск подозрительных фрагментов URI и дальнейшей обработкой внешними инструментами. В map {}проверяется переменная $request_uri. Нюанс в том, что если запрос вернул 400 ошибку, то в $request_uri вместо оригинального значения будет “-“.
Ожидал оригинального значения, исходя из описания в документации:
$request_uri
первоначальный URI запроса целиком (с аргументами), не изменяется в процессе обработки запроса;
При этом в $request оригинальный URI запроса отображается.
Так задумано или это баг?
Если ошибки нет, то как мне получить URI в сыром виде, даже если он некорректный? Обратывать $request не очень хотелось бы, так как он содержит лишние данные (метод, протокол).
Так и задумано. Если URI корректно обработать не удалось, то соответствующие переменные не инициализируются. Это также защищает пользователя и модули от попыток использования некорректного URI где-либо в процессе обработки.
400-ую ошибку стоит воспринимать как отсутствие какого-либо валидного запроса. Вам могут там вообще прислать бинарный мусор, где в принципе нет ничего похожего на URI. Какой смысл пытаться это анализировать?
Использовать переменную $request для чего-то помимо логов - вообще плохая идея. И работать для современных протоколов не будет. В HTTP/2 и HTTP/3 никакой строки запроса не существует, а она искусственно конструируется похожей на HTTP/1.1 исключительно для целей логирования и только после успешного парсинга всего запроса.
В такой же ситуации, но только для HTTP/2+ она будет также пустой.
Да и переменную $request_uri не рекомендуется использовать. Учтите, что любой “подозрительный фрагмент” может быть там спрятан за URI-кодировкой. Рассматривать же все URI, содержащие закодированные символы, как подозрительные - идея сомнительная.
Правильно ли я понимаю, что не рекомендуются с точки зрения логики работы современных протоколов HTTP/2+, а не безопасности или производительности?
Поступает множество запросов от плохил ботов, которые сканируют пути типа /.env, /.git и т.п. Все они доходят до приложения и создают лишнюю нагрузку.
Я решил выделить наиболее частотные вредные запросы и привентивно возвращать 404 ошибку, не отдавая на обработку python-приложению.
Пример:
map $request_uri $early_404 {
default 0;
~*/\.env 1;
~*/\.git/config 1;
~*/[0-9_]*?phpmyadmin[^/]*/([^/]+/)?(index|setup)\.php 1;
~*cgi-bin.*bin/sh 1;
# и так далее
}
И так же я хотел блокировать IP адреса, которые ломятся по этим путям. И чтобы не дублировать регулярки из конфига angie в фильтры fail2ban, решил вынести значение $early_404 в лог susp=”$early_404”. В f2b остается только чекнуть susp=”1”.
Единственная проблема, что в случае запросов типа /cgi-bin/%%32%65%%32%65/%%32%65%%32%65/%%32%65%%32%65/%%32%65%%32%65/%%32%65%%32%65/%%32%65%%32%65/%%32%65%%32%65/bin/sh IP адреса не банятся, так как в susp=””.
Нормально ли если я укажу в map $request вместо $request_uri? Или то, что я делаю - плохая пратика и есть более подходящие решения?
С точки зрения безопасности, т.к. это сырой URI, который не был декодирован и нормализован. Ограничения и проверки построенные на значении этой переменной может быть легко обойти путем манипуляция с URI-кодированием, двойными слешами и разными другими хитростями.
Ну вот ваши регулярки уже не сработают стоит лишь закодировать любой один символ (ту же точку). Почему бы не использовать нормализованный и декодированный $uri?
Если это 400, то смысл какой его проверять? Это заведомо плохой запрос, который нормальные клиенты не делают.
Только для того, чтобы заблокировать через fail2ban, если запрос подошел под условия.
Это если всё работает корректно на сайте
Не исключаю сценарий, что на прод может уйти изменение, которое будет отправлять невалидный запрос. И если я перейду на блокировку IP адресов, генерируюших N кол-во 400 ошибок, то могут полететь falsepositive и я перебаню нормальных посетителей.
Полезно для меня, спасибо!)
Единственный минус, который мне мешает использовать $uri - отсутствие ? query string. С другой стороны $uri учитывает больше сценариев.
@VBart в целом то, что я делаю с map и указанием “неугодных URL“ - это адекватная практика или лучше не изобретать велосипед и воспользоваться WAF решениями? Я так понимаю, что тот же Modsecurity можно настроить на нужные мне сценарии + будет защита от других более серьезных атак. Или это избыточно в текущем контексте, особенно на старте?
При этом нет цели сделать максимальное покрытие всезвозможных случаев. Это мера для среза трафика от скрипт-кидди с целью снижения нагрузки на приложение и очистки лога от явного мусора.
Обычно за валидность запросов отвечают готовые клиентские библиотеки для работы с HTTP и мало кто занимается тем, что сам программирует HTTP-протокол.
Я бы с куда большей вероятностью рассматривал сиутацию, что с таким подходом вы побаните нормальных посетителей просто из-за этого, что те, кто гоняет ботов - регулярно меняют IP-адреса, а потом эти IP-адреса достаются нормальным пользователям, а вы их уже забанили.
Никто не мешает добавить туда аргументы: $uri$is_args$args. Но имхо проверять аргументы - так себе идея, ибо всякие системы отслеживания пользователей туда что только не добавляют. И пользователь, просто перейдя по ссылке на стороннем сайте, получит в нагрузку огромный букет каких-нибудь аргументов.
Смотря какую проблему вы пытаетесь решить. У любого решения будут свои минусы в придачу: большая нагрузка на сервер, снижение стабильности и ложные срабатывания.
Будут ли какие-то плюсы при этом - зависит от конкретного сценария на мой взгляд. Если код бекендов пишут толпы студентов за еду, огромная корпорация, где вообще непонятно, что происходит или это черный ящик, который написал кто-то с нестабильной психикой, а потом уволился, то наверное стоит задуматься. Или если наняли целый отдел ИБ и они требуют WAF для галочки - также отдельный случай.
В быту я чаще сталкиваюсь с двумя парадоксальными ситуациями: WAF есть, но безголовые программисты забыли где-то добавить проверку пользователя и в результате приватные файлы пользователей можно запрашивать напрямую без авторизации, либо WAF который просто мешает обычным пользователям постоянными капчами.
В своё время в nginx посмеивались над Modsecurity, т.к. он был написан настолько отвратно и там регулярно правили сигфолты и зацикливания, что получить уязвимость просто установкой Modsecurity можно было куда проще, чем какую-то дополнительную безопасность.
Тут стоит ответить самому себе на вопрос: а они действительно какую-то существенную нагрузку создают и превалируют в логе или может быть нет с этим проблемы, а это попытка оптимизации ради оптимизации.
Если бекенды так сильно перегружаются от запроса несуществующих страниц, то тут стоит наверное с этим что-то сделать. Ведь иначе их можно перегрузить и запросами, которые не будут похожи на запросы ботов. И если бекенды настолько ограничены в ресурсах, то тут стоит подумать в сторону настройки каких-то общих лимитов с помощью тех же limit_req/limit_conn, а также добавить кэширование, в том числе 404 страниц.