Comments 27
Незнаю как у остальных, раньше не интересовался, но у меня касательно этого «пунктик». Всегда провожу тестирование не только рабочего процесса, а и симулирую неверные запросы, чтобы сайт/приложение работал как часики.
Это интересно. Ещё есть забавный момент с передачей двух заголовков Host. В случае неправильной (той, которая по умолчанию, и в мануалах в интернетах) конфигурации nginx, он увидел первый заголовок, а на бэк-энд отправит второй.
Надо ли понимать это как то, что никто в интернете, в том числе сам Сысоев, не знает, как правильно настраивать nginx, а знаете только вы?
Ну так тогда поделитесь, что там неправильного и как должно быть.
Ну так тогда поделитесь, что там неправильного и как должно быть.
В случае неправильной (той, которая по умолчанию, и в мануалах в интернетах) конфигурации nginx, он увидел первый заголовок, а на бэк-энд отправит второй.Покажите мне такую конфигурацию, при которой на бэкенд будет отправлен второй заголовок.
На примере nginx + fastcgi + php-fpm устоит?
Берём fastcgi_params из wiki.nginx.org/PHPFcgiExample (соответствует конфигу по умолчанию)
В конфиге хоста пишем примерно следующее:
Скрипт index.php выводит некоторые переменные из массива $_SERVER, включая HTTP_HOST.
Делаем запрос:
В ответе видим: HTTP_HOST=evil.com
Т.е. nginx в переменной server_name видел xenv.darkbyte.ru, а PHP увидел уже evil.com.
Если добавить строку: fastcgi_param HTTP_HOST $host, то проблема решается, PHP видит тоже самое, что и nginx.
Это можно проверить, сделав такой же запрос, но в первом Host указать env.darkbyte.ru.
Если поменять заголовки местами, то будет 404, ибо evil.com у меня на сервере не объявлен.
Берём fastcgi_params из wiki.nginx.org/PHPFcgiExample (соответствует конфигу по умолчанию)
В конфиге хоста пишем примерно следующее:
server_name xenv.darkbyte.ru;
location / {
fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
}
Скрипт index.php выводит некоторые переменные из массива $_SERVER, включая HTTP_HOST.
Делаем запрос:
# nc darkbyte.ru 80
GET / HTTP/1.1
Host: xenv.darkbyte.ru
Host: evil.com
Connection: close
В ответе видим: HTTP_HOST=evil.com
Т.е. nginx в переменной server_name видел xenv.darkbyte.ru, а PHP увидел уже evil.com.
Если добавить строку: fastcgi_param HTTP_HOST $host, то проблема решается, PHP видит тоже самое, что и nginx.
Это можно проверить, сделав такой же запрос, но в первом Host указать env.darkbyte.ru.
Если поменять заголовки местами, то будет 404, ибо evil.com у меня на сервере не объявлен.
Так и причем тут nginx? Параметры «HTTP_*» — это маппинг http-заголовков один к одному. Nginx совершенно справедливо передает все заголовки, что были в запросе, включая и оба заголовка «Host:». То, что php видит только последний — исключительно беда php, а может быть и беда конкретно php-fpm. В любом случае, программист, который обращается к HTTP_* должен ожидать там всё, что угодно.
Посмотрите тем же tcpdump-ом хотя бы, что именно nginx отправил.
Посмотрите тем же tcpdump-ом хотя бы, что именно nginx отправил.
Возможно оно так и есть, но ведь решить проблему можно только на уровне nginx. И в конфиге по умолчанию, который так же предлагается во многих мануалах по настройке nginx+php-fpm, этот момент упущен.
А tcpdump умеет слушать файловые сокеты? Если нет, то чем можно? Сходу не смог найти подходящей утилиты.
А tcpdump умеет слушать файловые сокеты? Если нет, то чем можно? Сходу не смог найти подходящей утилиты.
1. Что у вас за приложение такое, что ему нужен
Оставьте с хостами разбираться веб-серверу, не надо этим в приложении заниматься. Сегодня
2.
3. Каждый второй школьник, впервые в жизни настроивший что-то на локалхосте, скорее пишет мануал или руководство. Чему тут удивляться? 9 из 10 плохи в той или иной степени, отнюдь не из-за
HTTP_HOST
? Взяли вы netcat и специально послали два заголовка, получили 404. Нормальные браузеры так себя не ведут. В чем состоит проблема?Оставьте с хостами разбираться веб-серверу, не надо этим в приложении заниматься. Сегодня
Host
, а завтра ещё какой-нибудь заголовок. Я так подозреваю, что эффект будет одинаковый в отношении любого заголовка: php-скрипт увидит только последний из присланных. Содержимое HTTP_
— это в любом случае данные полученные от пользователя, и программист, который не выучил, что нельзя доверять данным присланным пользователем — просто некомпетентен. Возможно, что это повод вообще отказаться от такого приложения, безоспасность которого опирается на HTTP_*
.2.
fastcgi_params
содержит исключительно подборку переменных окружения из RFC 3875 и не более.3. Каждый второй школьник, впервые в жизни настроивший что-то на локалхосте, скорее пишет мануал или руководство. Чему тут удивляться? 9 из 10 плохи в той или иной степени, отнюдь не из-за
HTTP_HOST
.А tcpdump умеет слушать файловые сокеты? Если нет, то чем можно? Сходу не смог найти подходящей утилиты.Можно strace-ом посмотреть.
Перенастроил fastcgi на обычные сокеты, посмотрел трафик — действительно, передаётся оба заголовка HTTP_HOST. Но опять таки, то, что nginx и php в разном порядке рассматривают заголовки — это не правильно, как мне кажется.
Согласен, что доверять данным, полученным от пользователя нельзя. Но от этого не становится меньше уязвимых приложений. И если есть возможность на уровне веб сервера хоть немного прикрыть дыры приложений, которые за ним прячутся, то я не упускаю такой возможности.
Конечно браузер не будет посылать два заголовка Host, но ведь не только браузеры заходят на сайты. Внимание на данную проблему я обратил после того, как на одном сайте обнаружил «хитрый» механизм входа в админку, основанный на домене. Авторизация для входа на поддомен админки была настроена в nginx, а проверка домена в самом приложении.
Согласен, что доверять данным, полученным от пользователя нельзя. Но от этого не становится меньше уязвимых приложений. И если есть возможность на уровне веб сервера хоть немного прикрыть дыры приложений, которые за ним прячутся, то я не упускаю такой возможности.
Конечно браузер не будет посылать два заголовка Host, но ведь не только браузеры заходят на сайты. Внимание на данную проблему я обратил после того, как на одном сайте обнаружил «хитрый» механизм входа в админку, основанный на домене. Авторизация для входа на поддомен админки была настроена в nginx, а проверка домена в самом приложении.
И если есть возможность на уровне веб сервера хоть немного прикрыть дыры приложений, которые за ним прячутся, то я не упускаю такой возможности.
Да, но наверное не стоит при этом упрекать всех, кто использует nginx просто по прямому назначению, а не для валидации входных данных ради небезопасных приложений. Возможности nginx в этом смысле достаточно велики даже из коробки. Можно понаписать регулярок, и ещё проверять
$args
на SQL инъекции. А особо озабоченные проблемой люди даже специальный модуль разработали. Но это палка о двух концах.Я даже специально ещё раз подчеркну, что вообще присутствие
HTTP_HOST
среди переменных окружения никто не обещал. Попытка её активно использовать — это уже нестандарт, и если вам действительно нужно как-то пробрасывать относительно валидный хост на бэкенд и вы по каким-то причинам очень не желаете использовать SERVER_NAME (специально для того предназначенную, и описанную в спецификации с словом MUST), то лучше для этого придумать какую-нибудь свою переменную, не пересекающуюся с заголовками:fastcgi_param MY_HOST xenv.darkbyte.ru;
— будет намного правильнее.Хорошо, когда за nginx находятся свои приложения, которым можно доверять. Но бывает, что ставятся чужие решения, и не всегда они достаточно качественные, чтобы просто их поставить и забыть.
Пример с авторизацией через поддомен — это частный случай. Но можно взять более распространённый пример — сбор статистики средствами самого движка. Очень часто приходится сталкиваться с тем, что переменной HTTP_HOST доверяют, считая, что раз веб сервер отправил на их сайт запрос, значит в HOST будет точно их хост.
Опять же, одно дело дорогие регулярки и модули, а другое дело, практически бесплатный костыль :)
В моём случае, в server_name написано "~^((.*)\.)*(.+\..+)$", а валидный хост передаётся отдельным заголовком.
Пример с авторизацией через поддомен — это частный случай. Но можно взять более распространённый пример — сбор статистики средствами самого движка. Очень часто приходится сталкиваться с тем, что переменной HTTP_HOST доверяют, считая, что раз веб сервер отправил на их сайт запрос, значит в HOST будет точно их хост.
Опять же, одно дело дорогие регулярки и модули, а другое дело, практически бесплатный костыль :)
В моём случае, в server_name написано "~^((.*)\.)*(.+\..+)$", а валидный хост передаётся отдельным заголовком.
Вот интересно почему нет массовой популярности у lighttpd как у nginx? Неужели последний гораздо круче?
да
немного странно вообще сравнивать эти две вещи — nginx мощен тем что поддерживает всяко кэширование, прокси, балансировку, и почти всё что умеет апач.
а lighttpd — это веб-сервер — который поддерживает те технологии, которые нужны современному лёгкому веб-серверу.
а lighttpd — это веб-сервер — который поддерживает те технологии, которые нужны современному лёгкому веб-серверу.
>> а lighttpd — это веб-сервер — который поддерживает те технологии, которые нужны современному лёгкому веб-серверу.
Что не мешает ему также поддерживать «кэширование, прокси, балансировку, и почти всё что умеет апач.»
Что не мешает ему также поддерживать «кэширование, прокси, балансировку, и почти всё что умеет апач.»
Тогда он становится не таким уж и легким :)
Ну а если серьезно, то вот человек делал обзор blog.monitis.com/index.php/2012/08/22/nginx-vs-lighttpd/ — все правильно пишет в последнем абзаце.
Ну а если серьезно, то вот человек делал обзор blog.monitis.com/index.php/2012/08/22/nginx-vs-lighttpd/ — все правильно пишет в последнем абзаце.
Разработка lighttpd 1.x прекращена много лет назад (только иногда багфиксы случаются). А релиз lighttpd 2.0 так и не увидел свет.
Первое что приходит в голову: передать неправильный заголовок Host:, состоящий из правильного имени хоста, нулевого байта и мусора.
На третьем запросе сервера «посыпались». И нет ни одного ответа «HTTP/1.0 200 OK».И не должно быть.
Остальные же результаты тестов зависят от настройки самих серверов, каждый волен настраивать как того желает.
Исключение составили DevConf, e-Legion Ltd. и Intel. Первые два используют nginx, поэтому проблема, скорее всего, именно в его настройкеНет, проблема может быть, и скорее всего, в бэкенде. Nginx сам никогда не выдаст
INTERNAL SERVER ERROR
в верхнем регистре.На третьем запросе сервера «посыпались». И нет ни одного ответа «HTTP/1.0 200 OK».
И не должно быть.
Я пожалуй даже поясню этот пункт, ибо видимо не все понимают.
Кто-то видимо ошибочно полагает, что указываемая в запросе или ответе версия протокола — это версия протокола запроса или ответа (использумая при формировании самого запроса или ответа). Нет, это заблуждение.
Этот механизм нужен, чтобы сервер и клиент сообщили друг-другу наивысшую версию протокола, с которой они совместимы. Если клиент отправил HTTP/1.0 запрос, то nginx не будет использовать ничего в ответе, что могло бы оказаться несовместимым с HTTP/1.0 (такие вещи, например, как chunked encoding).
Подробнее читайте в RFC 2145.
P.S. Кстати, не стоит ещё удивляться также, что в запросах без хоста (в строке запроса или заголовке Host) в принципе не работает виртуальный хостинг.
> На третьем запросе сервера «посыпались». И нет ни одного ответа «HTTP/1.0 200 OK».
Я там отдаю 403 Forbidden. Сервер не должен отвечать на запрос, в котором нет корректного хоста. См. DNS Rebinding.
Я там отдаю 403 Forbidden. Сервер не должен отвечать на запрос, в котором нет корректного хоста. См. DNS Rebinding.
Неправильно.
HTTP/1.1 — это не 403 Forbidden, а именно 400 Bad Request. Потому, что это неправильно составленный запрос
В HTTP/1.0 заголовок Host необязателен, так что GET / HTTP/1.0 без Host — вполне валидный запрос
HTTP/1.1 — это не 403 Forbidden, а именно 400 Bad Request. Потому, что это неправильно составленный запрос
В HTTP/1.0 заголовок Host необязателен, так что GET / HTTP/1.0 без Host — вполне валидный запрос
С точки зрения стандартов Вы правы.
Но это ещё и вопрос безопасности. Во избежание атак DNS Rebinding запросы, не направленные на правильный хост (или его поддомены), не должны отдавать ничего хорошего. Иначе это потенциальная дыра.
Другой вопрос — будут ли клиенты, умеющие только HTTP/1.0, кэшировать результаты DNS-запросов (тем самым добавляя «клиентскую сторону» уязвимости). Мы никак не можем гарантировать, что не будут. Даже если клиент этого не делает, может влезть nscd или что-то в этом роде.
Статический сайт с общедоступной информацией и отсутствием логина/форм/скриптов может от DNS Rebinding и не защищаться. Перечисленные в Вашем посте сервисы — обязаны.
Но это ещё и вопрос безопасности. Во избежание атак DNS Rebinding запросы, не направленные на правильный хост (или его поддомены), не должны отдавать ничего хорошего. Иначе это потенциальная дыра.
Другой вопрос — будут ли клиенты, умеющие только HTTP/1.0, кэшировать результаты DNS-запросов (тем самым добавляя «клиентскую сторону» уязвимости). Мы никак не можем гарантировать, что не будут. Даже если клиент этого не делает, может влезть nscd или что-то в этом роде.
Статический сайт с общедоступной информацией и отсутствием логина/форм/скриптов может от DNS Rebinding и не защищаться. Перечисленные в Вашем посте сервисы — обязаны.
Во-первых, пост не мой.
Во вторых, кэширование dns-ответов на клиентской системе как раз наоборот блокирует атаку. Если у меня ответ 192.0.2.12 закэшировался в браузере, атакующий может хоть кол на голове dns-сервера тесать и подставлять в зону любые адреса, хоть 127.0.0.1 — мой браузер будет обращаться туда, куда закэшировал.
В третьих, те серверы, которые в реальности могли бы пострадать, не удастся настроить таким образом. Я имею ввиду веб-интерфейсы, которые действительно находятся в локальной сети: модемы, nas, точки доступа, веб-камеры и прочее подобное оборудование. Однако, чтобы с ними что-то сделать, нужно авторизоваться, что сделать будет сложно.
А в четвёртых, те веб-серверы, которые всё-таки можно так настроить, обычно находятся не в локальной сети, к ним можно сделать запрос без Host или с неверным Host и безо всякой DNS Rebinding. Они могут в ответ на HTTP/1.0 без Host: выдавать не 403, а 302, 204, 205, или даже 200 с объяснением, что происходит — мы получим полное соответствие протоколу и отсутствие возможности такой атаки. В моём случае клиент обычно увидит default virtualhost, который говорит «It works!» — какая уж тут уязвимость.
Во вторых, кэширование dns-ответов на клиентской системе как раз наоборот блокирует атаку. Если у меня ответ 192.0.2.12 закэшировался в браузере, атакующий может хоть кол на голове dns-сервера тесать и подставлять в зону любые адреса, хоть 127.0.0.1 — мой браузер будет обращаться туда, куда закэшировал.
В третьих, те серверы, которые в реальности могли бы пострадать, не удастся настроить таким образом. Я имею ввиду веб-интерфейсы, которые действительно находятся в локальной сети: модемы, nas, точки доступа, веб-камеры и прочее подобное оборудование. Однако, чтобы с ними что-то сделать, нужно авторизоваться, что сделать будет сложно.
А в четвёртых, те веб-серверы, которые всё-таки можно так настроить, обычно находятся не в локальной сети, к ним можно сделать запрос без Host или с неверным Host и безо всякой DNS Rebinding. Они могут в ответ на HTTP/1.0 без Host: выдавать не 403, а 302, 204, 205, или даже 200 с объяснением, что происходит — мы получим полное соответствие протоколу и отсутствие возможности такой атаки. В моём случае клиент обычно увидит default virtualhost, который говорит «It works!» — какая уж тут уязвимость.
Sign up to leave a comment.
Двуликий REQUEST_URI или в поисках корректного HTTP/1.1 сервера