Re: proxy cache key и fastcgi cache key

Gena Makhomed gmm at csdoc.com
Fri Jan 10 11:45:27 UTC 2014


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 все что угодно.

А ведь именно на основании значения переменной $host
nginx и принимает решение в какой server направить запрос
на обработку. Но к FastCGI уходит не $host, с которым
работал nginx принимая решение, а fake-значение из $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%.
Я и сам так считал до недавнего времени, доверяя документации 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

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

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

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

путаницы с этим хватает: https://stackoverflow.com/questions/1459739/

Поэтому те кто пишут на php не могут доверять значению SERVER_NAME,
и у них остается единственный вариант - только переменная HTTP_HOST.

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

Они проверяют и валидируют значение HTTP_HOST средствами nginx,
так как это прописано в документации к nginx, и рекомендовано 
разработчиками nginx. Зачем два раза проверять одно и то же?

Как будто мало разработчикам проблем с самим php
и его глюками, например, cgi.fix_pathinfo 1 по умолчанию:
http://habrahabr.ru/post/100961/#comment_3125990

Так теперь еще и с nginx при использовании php-fpm
эти глюки. А ведь php-fpm это наверное основной уже
способ как использовать php и nginx.

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

"Самый эффективный способ защиты — явно определить 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 и забыть про проблему.

Способ №5 самый простой в реализации и 100% эффективный метод защиты.

Почему "нет" ?

P.S.

Например, вот сколько "магии" наворотили в модуле кеширования,
и незаметное (недокументированное) трансформирование HEAD в GET,
и незаметное (недокументированное) вырезание заголовков
If-Modified-Since и If-None-Match и выключение кеширования,
если в ответе присутствует заголовко Set-Cookie и т.д. и т.п.

А такую мелочь с передачей на backend валидного значения $host,
которая никому не будет мешать и только принесет max. пользу...

-- 
Best regards,
  Gena



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