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