Re: proxy cache key и fastcgi cache key

Валентин Бартенев vbart at nginx.com
Fri Jan 10 12:53:26 UTC 2014


On Friday 10 January 2014 13:45:27 Gena Makhomed wrote:
> On 10.01.2014 12:24, Валентин Бартенев wrote:
> >>> Кому интересно почитать, подробней вот ссылка.
> >>> http://habrahabr.ru/post/166855/
> >>> 
> >>> Как видите, корректное значения имеют только переменные $host и
> >>> $server_name, все что основывается на $http_host имеет потенциал
> >>> уязвимость, если бекенд доверяет этой переменой, лично я знаю несколько
> >>> популярных РНР фрейморков которые используют эту переменную без проверки
> >>> и без экранирования в SQL запросах.
> >> 
> >> и пофиксить эту проблему можно в исходниках nginx таким образом,
> >> что если вдруг в переменных $host и $http_host оказываются разные
> >> значения, чтобы nginx в $http_host записывал значение из $host.
> >> 
> >> тогда после установки следующего обновления nginx эта уязвимость
> >> в backend`ах автоматически пофиксится у всех пользователей nginx.
> >> 
> >> причем без какой-либо необходимости пользователям править
> >> файлы fastcgi.conf / fastcgi_params и все производные от них.
> > 
> > Так, между делом, хочу напомнить, что на CGI есть спецификация,
> > описывающая
> > все переменные окружения, которые сервер должен передавать приложению.
> > И в ней вполне черным по белому сказано, что все переменные HTTP_* это
> > protocol specific переменные полученные из заголовков переданных клиентом.
> 
> fastcgi_param	HTTP_HOST1		$http_host;
> fastcgi_param	HTTP_HOST2		$host;
> fastcgi_param	HTTP_HOST3		$server_name;
> 
> Делаем запрос:
> GET http://site3.dev/phpinfo.php HTTP/1.1
> Host:~%#$^&*()<>?@\!."'{}[]=+|
> 
> На выходе получим
> _SERVER["HTTP_HOST1"]: ~%#$^&*()<>?@\!."'{}[]=+|
> _SERVER["HTTP_HOST2"]: site3.dev
> _SERVER["HTTP_HOST3"]: site2.dev
> 
> В переменной $host правильное значение,
> в переменной $http_host все что угодно.

Что значит "правильное"?  С точки зрения спецификации,
правильное значение HTTP_HOST1 - это значение из заголовка
Host1 (с учетом оговоренных преобразований) и никак иначе
не надо трактовать переменные HTTP_* они имеют вполне
специфическое назначение - а именно получить raw-данные из
запроса.

И если обратить внимание, то в конфигурации вообще нигде не указывается

   fastcgi_param	HTTP_HOST $http_host;

nginx, как и положено, автоматически преобразует заголовки в HTTP_*
переменные.

Если заголовка Host в запросе вообще не будет, то и переменной HTTP_HOST
быть также не должно.


> А ведь именно на основании значения переменной $host
> nginx и принимает решение в какой server направить запрос
> на обработку. Но к FastCGI уходит не $host, с которым
> работал nginx принимая решение, а fake-значение из $http_host

Если мы говорим про переменную HTTP_HOST, так и должно быть.


> Они то protocol specific, но раз запрос клиента попал в server
> где в server_name прописано site3.dev - клиент искренне полагает,

Клиент?

> что надлежащую проверку уже провел nginx, ведь в конфиге пользователь
> прописал:
> 
> server {
>      listen      80 default_server;
>      return      444;
> }
> 
> B значит все другие значения HTTP_HOST, которые не соответствуют
> нормальным значениям server_name должны попадать в этот server-заглушку.

Значит из чего?  Из того, что кто-то так полагает?  Неправильно полагает.


> Поэтому валидировать еще раз то, что и так уже провалидировал nginx
> никакого смысла нет. С точки зрения обычных пользователей, коих 99.999%.

Обычных пользователей?  Может быть, но приложения пишут, минуточку, не
обычные пользователи, а программисты, что предполагает наличие каких-то 
профессиональных навыков, нет?  Если программист использует какую-то
переменную не понимая её назначения, то едва ли ему можно чем-то помочь,
проблемы у него возникнут не только с HTTP_HOST, а и в 1000 других мест.


> Я и сам так считал до недавнего времени, доверяя документации nginx:
> 
> http://nginx.org/en/docs/http/request_processing.html
> 
> http://nginx.org/en/docs/http/server_names.html
> 
> http://nginx.org/en/docs/http/ngx_http_core_module.html#server_name

Где в документации что-то написано о:

> B значит все другие значения HTTP_HOST, которые не соответствуют
> нормальным значениям server_name должны попадать в этот server-заглушку

?


> 
> > И есть безопасная и специфицированная переменная SERVER_NAME.
> 
> она есть такая только для FastCGI,

Она есть такая во всех CGI-подобных протоколах.


> а для proxy_pass на апач - все не так просто, если погуглить
> server_name http_host site:bugs.php.net

Поэтому умолчания для proxy_* и fastcgi_* отличаются.


> тем более, что в SERVER_NAME со стороны веб-сервера
> будет приезжать каноническое имя сервера, а это обычно hostname.
> и для всех виртуальных хостов будет одинаковое значение SERVER_NAME.

Виртуальный хост с точки зрения конфигурации nginx - это одна секция
блока server.  Если пользователь руководствуется другой идеологией,
то должен соответствующим образом исправить конфигурацию.


> путаницы с этим хватает: https://stackoverflow.com/questions/1459739/
> 
> Поэтому те кто пишут на php не могут доверять значению SERVER_NAME,
> и у них остается единственный вариант - только переменная HTTP_HOST.

И если они используют переменную HTTP_HOST, то они её должны соответствующим
образом проверять.  Почему те, кто пишут на других языках, это понимают, а
"те кто пишут на php" - это такая особая категория, им нужно особое 
снисхождение и сочувствие?


> > Если кто-то в приложении использует данные полученные от клиента без
> > надлежащей проверки, когда в любой книжке "web-programming for dummies"
> > написано по 5 раз, что не следует доверять этим данным, то что я могу
> > предложить?  Расстрел.
> 
> Они проверяют и валидируют значение HTTP_HOST средствами nginx,
> так как это прописано в документации к nginx, и рекомендовано
> разработчиками nginx. Зачем два раза проверять одно и то же?

Где? Цитату из документации, где прописано, что HTTP_HOST валидируется?


> Как будто мало разработчикам проблем с самим php
> и его глюками, например, cgi.fix_pathinfo 1 по умолчанию:
> http://habrahabr.ru/post/100961/#comment_3125990
> 
> Так теперь еще и с nginx при использовании php-fpm
> эти глюки. А ведь php-fpm это наверное основной уже
> способ как использовать php и nginx.

Следование спецификации это сейчас глюком называется?  Чем в этом отношении 
отличается любой другой сервер?  Вы хотите сказать, что в случае того же 
mod_fastcgi в HTTP_HOST будет что-то другое?

Даже в документации по PHP написано: 
http://www.php.net/manual/en/reserved.variables.server.php

'HTTP_HOST'
    Contents of the Host: header from the current request, if there is one.

Где тут что-то про валидацию сервером?


> Попробую иначе спросить: что может поломать предложенный
> мной выше fix, который будет закрывать эту уязвимость в backend`ах,
> которые доверяют значению переменной HTTP_HOST полученной от nginx?

Все нормальные приложения, которые рассчитывают получить в HTTP_HOST сырое
значение из заголовка и далее использовать его соответствующим образом.

Допустим ваше приложение занимается выявлением распространенных атак и 
превентивно блокирует злоумышленников.  Или задача вашего приложения выводить
или логгировать заголовки клиента.

Пример одного из самых распространенных приложений: phpinfo.php - начинает
отображать _неверное_ значение HTTP_HOST.


> "Самый эффективный способ защиты — явно определить HTTP_HOST на стороне
> веб сервера." - цитата из статьи http://habrahabr.ru/post/166855/

Защиты от чего?  От кривых рук и дилетантов защиты не существует в принципе.


> Сейчас же nginx по-умолчанию отправляет на backend
> значения $proxy_host и $http_host вместо ожидаемого там $host.
>
> Если глюк с $proxy_host очевиден, то глюк с $http_host совсем нет.
> 
> Способы решения проблемы:
> 
> 1. Расстрелять всех, кто пишет кривой код. - Нереально.
> 
> 2. Исправить все глюки и весь кривой код. - Нереально.
> 
> 3. Всем вручную добавить fix в fastcgi_param HTTP_HOST $host;
> 
> 4. Добавить этот fix в дефолтовые конфиги nginx.
> 
> 5. Исправить это один раз в коде nginx и забыть про проблему.

И создать другую, на мой взгляд ещё большую проблему на века.
Оригинальная проблема не исправляется "фиксом" в конфигурацию.
Ибо оригинальная проблема, как было выше указано - кривой код,
который сломается сразу в другой конфигурации и/или на другом
сервере.

Предлагать исправить переменную HTTP_HOST, это то же самое,
что предлагать сразу исправить переменную $http_host на основании
того, что кто-то там что-то полагает.

Да, кстати, а почему действительно предложение исправить HTTP_HOST,
а не $http_host?

Уверен, найдется не одна конфигурация, где люди используют значение
$http_host в директиве alias например.  Может и их нужно защитить?
Чем они хуже особой категории "те кто пишут на php"?

--
Валентин Бартенев


Подробная информация о списке рассылки nginx-ru