Re: Об одной малоизвестной уязвимости в веб сайтах
Maxim Dounin
mdounin at mdounin.ru
Tue Jun 17 07:00:31 UTC 2014
Hello!
On Mon, Jun 16, 2014 at 03:36:38PM -0400, S.A.N wrote:
> > > Maxim Dounin Wrote:
> > > -------------------------------------------------------
> > > > Какая-либо проблема появляется тогда и только
> > > > тогда, когда администратор начинает писать в конфиге $http_host,
> > > > не думая о последствиях. Есть мнение, что совсем простое решение
> > > > этой проблемы - не делать так (c) анекдот.
> > >
> > > Вся проблема в том что сами программисты Nginx в модуле FastCGI
> > используют
> > > переменную $http_host для HTTP_HOST, вместо нормализованной
> > переменой $host,
> > > администраторам на оборот приходится исправлять это своими руками и
> > писать в
> > > конфиге
> > > fastcgi_param HTTP_HOST $host;
> > > Если следовать вашей логике, надо изменить код модуля FastCGI, чтобы
> > по
> > > умолчанию Nginx был вполне себе безопасный.
> >
> > По умолчанию в fastcgi http-заголовки передаются как есть, в виде
> > параметров HTTP_*. Каноническое же имя сервера доступно в
> > параметре SERVER_NAME. Что из этого и как использовать - это
> > вопрос к приложению, а не к nginx'у.
>
> Я знаю, и уже выше объяснял почему бекенд-приложениям нужно использовать
> HTTP_HOST вместо SERVER_NAME.
> Значения SERVER_NAME не может использоваться, если в директиве server_name
> используется маска или бекенд обрабатывает запросы для default_server.
Если SERVER_NAME нельзя использовать - значит, необходимо передать
дополнительный параметр, который и использовать.
Использовать HTTP_* поля для чего-то, что позволяет "получить
доступ" - это неправильно.
> Ещё раз приведу пример, в котором мы легко можем получить доступ, к закрытым
> на уровне Nginx ресурсам.
>
> server
> {
> server_name *.example.com;
> ...
> fastcgi_pass php;
> }
>
> server
> {
> server_name private.example.com;
>
> location / {
> allow 192.168.1.0/24;
> deny all;
>
> fastcgi_pass php;
>
> auth_request /auth;
> }
> ...
> }
>
> Как видно два хоста используют один upstream, на котором бекенд приложения
> даёт расширенные привилегии юзерам private.example.com, в расчете на то что
> Nginx предварительно сделал проверку по IP и успешно провел аунтификацию,
> всё вроде логично и удобно сделано, но такая схема легко ломается таким
> запросом.
>
> GET http://example.com/SecureData/ HTTP/1.1
> Host: private.example.com
>
> Бекенд получает HTTP_HOST=private.example.com, даёт юзеру привилегии, но
> Nginx не проводил проверки по IP и не делал запрос аунтификацию, потому что
> отработал хост конфигурации для example.com вместо private.example.com
Проблема в том, что приложение некорректно предполагает, что
HTTP_HOST=private.example.com чем-то отличается от других. Как
показывает пример запроса выше - это не так. И "не так" - не
только в nginx'е, но и в других серверах.
Не надо себя обманывать и пытаться закрыть nginx'ом небезопасную
логику приложения - это не работает и рано или поздно выстрелит.
Правильное решение - передавать информацию о произошедшей
авторизации явно и отдельно (или пользоваться параметром
SERVER_NAME, который уже передаётся и предназначен специально для
идентификации сервера).
> В данном случаи можно было бы использовать переменную SERVER_NAME, но я выше
> писал почему это не всегда возможно.
>
> Если мы оба понимаем, что ситуация когда authority component и значения Host
> разные, это исключительная ситуация и её нужно обрабатывать как Exception, в
> этом случаи есть три варианта поведения, выбросить Exception (отдать 400
> статус), исправить ошибку (использовать переменую $host), и самый плохой
> вариант (антишаблон) ничего не делать и не обрабатывать эту исключительную
> ситуацию и оставить все как есть.
Возможно, когда-нибудь мы и придём к тому, что в таких ситуациях
будет возвращаться 400. Но это ни коим образом не избавляет от
необходимости исправить приложение.
--
Maxim Dounin
http://nginx.org/
Подробная информация о списке рассылки nginx-ru