Опасность слепой проверки заголовка X-Forwarded-For
15 апреля 2012
Сегодня пришёл pull-request в Yii на эту тему. В классе CHttpRequest
есть метод getUserHostAddress
, который отдаёт нам IP пользователя. Предложение было перед возвратом $_SERVER['REMOTE_ADDR']
проверить, а нет ли чего в $_SERVER['HTTP_X_FORWARDED_FOR']
, и, если есть — вернуть. Мотивация — будут видны IP пользователей за прокси.
Так как такое заблуждение встречается ну очень часто, я даже не сразу вспомнил, откуда берётся $_SERVER['HTTP_X_FORWARDED_FOR']
. А берётся он из заголовка HTTP запроса X-Forwarded-For
и означает это то, что придти нам может что угодно.
А теперь представим, что разработчик воспользовался изменённым методом getUserHostAddress
, проверяющим X-Forwarded-For
:
if(in_array(Yii::app()->request->getUserHostAddress(), $allowedAddresses)) { // даём доступ к админке }
Тем, кто пишет на Zend Framework, кстати, стоит проверить свой код потому как Zend_Controller_Request_Http::getClientIp()
без переданного аргументом false
как раз проверяет X-Forwarded-For
:
/** * Get the client's IP addres * * @param boolean $checkProxy * @return string */ public function getClientIp($checkProxy = true) { if ($checkProxy && $this->getServer('HTTP_CLIENT_IP') != null) { $ip = $this->getServer('HTTP_CLIENT_IP'); } else if ($checkProxy && $this->getServer('HTTP_X_FORWARDED_FOR') != null) { $ip = $this->getServer('HTTP_X_FORWARDED_FOR'); } else { $ip = $this->getServer('REMOTE_ADDR'); } return $ip; }
Заслал репорт в трекер Zend Framework на всякий случай.
Комментарии RSS по email OK
Согласен. Меня вообще удвиляет почему это поведение сделано по-умолчанию.
Кстати у вас небольше баг. Если сначала нажать на "Предварительный просмотр", значение чекбокса "Подписаться на комментарии" берется дефолтное (отлкючен), а не из POST'а.
Ну, и вы уверены, что капчу нужно вводить дважды?
Для предпросмотра можно не вводить. Дефолт исправил.
Но в самом yii можно и было сделать обертку для HTTP_X_FORWARDED_FOR в Yii::app()->request
Пишу сообщение, имя, почту, считаю капчу, о, есть предварительный просмотр. Кликаю, удоставеряюсь, что все ок, хочу оправить. Опять нужно считать капчу :(
Я эти оба адреса через пробел конкатенировал и все нормально было. Нечего опасаться. Может и в Yii так организовать ?
Максим, заголовок приходит сырой и не обработанный. Там может быть не только IP или IP-через-запятую, но и всякая другая муть типа <SCRIPT> или '; DROP TABLE Students
Я понял из поста, ну это уже на совести программера должно быть. Можно и сам IP адрес подделать и реферер и много всяких других полей, но, защита от xss и sql injection остается на совести программиста.
Расскажите как?
Дмитрий, капчу для предпросмотра можно просто не вводить.
Максим, IP подделать в общем случае нельзя, можно его только скрыть за прокси.
IP подделать нельзя, а вот в X-Forwarded-For можно записать всё, что душе угодно
Верно. Собственно, поэтому и использовать его в приведённом выше методе по умолчанию опасно.
2Sam, IP подделать таки можно, но крайне сложно + только в ограниченной, валидной форме.
Максим, полноценным запросом это назвать будет нельзя. Толку с него будет не много.
2 Максим
Что это значит? Не говорите загадками!
2Дмитрий, То есть, можно использовать атаку man in the middle и "сырые" сокеты чтобы выдать свой IP за чужой.. Но опять же, адрес все равно останется вида число.число.число.число . На практике это конечно труднореализуемо. Но, в сети полно туториалов по применению. 2Sam, бывают случаи когда авторизация привязана к IP адресу админу.
2Максим Спасибо. Стал читать про man in the middle и сырые сокеты, зацепился за SUID, SGID and the Sticky Bits - и стало стыдно, что я этого не знал раньше, хотя юзаю линукс повседневно уже 2-й год!
2 Максим
И почему "На практике это конечно труднореализуемо."?
То есть, XSS так не сделать как и sql-inj. Разве что специфическую защиту обойти и приватные данные получить. А труднореализуемо потому что есть файрвол от провайдера и arp таблицы, а также куча всяких других тонкостей. Поэтому такой тип атаки используется крайне редко. Но это уже не тема для обсуждения в этом блоге....
Ммм, давайт еще раз по порядку.
Не совсем понял, что вы имеете ввиду, ведь IPv4 именно такого формата!
ARP таблицы используются для преобразования ip в Ethernet-адрес. Не понимаю как это связано?!
if(in_array(Yii::app()->request->getUserHostAddress(), $allowedAddresses)) { // даём доступ к админке } Ужас. Просто ужас!
Максим, если XSS запихать в X-Forwarded-For, и этот IP где-то будет выводиться в голом виде(например в какой-нибудь статистике), то очень даже возможно. ARP, фаерволлы тут ни при чем. ОЛСО, они ни при чем только потому что, IP какбэ не в виде число.число.число.число передается, а в виде 4х байт и всё это происходит на уровне протокола,ядра и прочего, а не приложения. Не путайте людей, пожалуйста.
Итак, поясню, что тут пытались донести: 1. REMOTE_ADDR - IP сокета, который всегда IP, ибо его подставляет веб-сервер при соединении. Его не подделать никак, кроме man in the middle и иже с ним, т.е. не уровень приложения. 2. X-Forwarded-For - самый обычный заголовок. Его используют всякие proxy и балансировщики для того, чтобы показать, что за внешним IP, что по соединению, сидят другие люди, нежели gateway. Если веб-сервер(apache,nginx) сам не проверяет валидность заголовка(а валидный это ip или несколько ip через запятую), то проверять это надо на уровне приложения, ибо этот заголовок может быть специально сформирован клиентом. 3. Если на уровне веб-сервера не дай бог меняют REMOTE_ADDR на X-Forwarded-For - проверять надо и REMOTE_ADDR. Итог: проверяйте заголовки тоже!
Т.е. пормируя запрос используя сырые сокеты я не смогу подменить IP, я правильно понял?
Дмитрий, IP-спуфинг в основном действует только в локальной сети, где можно послать пакет напрямую получателю в обход роутера. В реальной жизни все современные роутеры исправляют пакет на лету и ставят там реальный адрес.
Если бы всё было так просто, то не было бы смысла во всех allow from ... в apache и т.д.
В догонку - неплохая статья, про один из методов, блокирующий ip-спуфинг suvan.ru/page/ip-spufing.html
2Bethrezen, А я разве где-то писал что файрвол фильтрует X-Forwarded-For(хотя на некоторых прокси и шлюзах он таки не передается) ? Я такого не писал. Так же как и я не писал что IP адрес в TCP-протоколе не в виде 4-х байт(или 6 в перспективе) передается, вы можете поспорить, но текстовое представление IP адреса стандартизировано: байт(число).байт.байт.байт. Я лишь писал про REMOTE_ADDR и как на некоторых сайтах, с помощью его подделки можно попасть в админку(встречал в одном проекте на cakephp).
Я понял о чем статья, и даже написал свое мнение - что это должно быть на совести разработчика. Я не ожидал что вы запутаетесь, потому как старался в своем комментарии использовать поменьше терминологии, так как это должно было упростить его понимание.
Максим, я то не запутался. Просто не надо тут приплетать MITM и спуфинг.
Bethrezen, Думаю Alexander правильно прокомментировал мой комментарий. На практике меня провайдер от сети отключал ненадолго за такие проделки. Благо заявления не написали. Но все же, я не мог не упомянуть что в своих скриптах нельзя слепо полагаться не только на X-Forwarded-For, но и на REMOTE_ADDR.
P.S.: Мы пишем об одном и том же, к чему нам спорить ? Вы хотите добавить что-то к моему комментарию - это ваше право.
IP адрес подделать нельзя , никак , это физически не возможно.
Можно скрыть его через vpn/socks proxy которые спалят свой физический адрес, но это другой вопрос, потому как тут прокси палит свой адрес
2Максим, 2Bethrezen
Вы вообще представляете как на физическом уровне происходит соединение ? Какой спуффиг вообще ? вы о чем ? Сами то статью свою читали ? Ну поменяете вы поле отправителя , ну пошлете [SYN] пакет, хост ответит [SYN,ACK] хосту чей адрес вы маскируете , ну а дальше как ? Может вы думаете угадать seq , при том, до того как поддельный хост отправит [RST] пакет ? То что роутеры пакеты исправляют полная чушь, они могут их либо сбрасывать либо пропускать ,без изменения, исправлять они не будут , потому как не "телепаты" чтоб угадать реальный адрес отправителя.
По поводу смены адреса при ARP cache poisoning, когда один узел притворяется шлюзом и для других узлов в сети проксирует запросы , то тут вообще атака не на уровне IP и работает исключительно в одноранговой сети где, узлы можно связать на физическом уровне, без роутинга да и то , далеко не всегда, особенно на современных ОС
Резюме - подделать IP адрес узла через который установлено соединение НЕВОЗМОЖНО
Шаря по интернету в поисках решения своей проблемы, наткнулась на эту статью. Я вижу, вы вполне понимаете, о чем говорите) Может, поможете мне решить мою проблему?
Три дня назад у меня отказал чат в одной он-лайн игре. Что только я ни делала, чтобы выяснить, в чем причина. Сначала грешила на браузер. Но смена браузера, вернее две смены, результата не дали. Чистка компа десятком различных антивирусников тоже не принесла плодов. Пробовала менять настройки соединения - безрезультатно.
В конце концов случайно залезла во вкладку "Визиты" в той браузерке, где фиксируются ip-адреса пользователей, и там обнаружила, что именно с 27 апреля эта игра определяет мой ip не как (83.149..), как это было ранее, а как (83.149..) forwarded for: (10.211..). То есть, насколько я понимаю, теперь я попадаю в игру через прокси.
Как такое могло произойти, если никакими проксями я не пользовалась? Может ли такое действие производить какой-нибудь вирус? И наконец, как от этого избавиться?
Такое легко могло произойти по вине провайдера. Не редко они ставят "прозрачные" прокси для снижения нагрузок, или садят пользователей за NAT. Решается звонком провайдеру и выяснением отношений, по какому они поводу начали модифицировать ваш траффик. Желательно почитать предварительно договор , чтоб убедиться, на что они имеют право а на что нет.
Спасибо за совет) Как раз хотела им позвонить.
Позвонила. Они признались, что это их идея, но при этом сообщили, что прокси отключать нельзя, ибо от этого интернет вообще отключится. На закономерный вопрос, почему же раньше без прокси обходились и ничего не отрубалось, невразумительно молчат.
Читайте договор, там должен быть пункт относительно модификации траффика, вообще они не имеют на это право , если не оговорено обратное. Отключить всегда можно, было бы желание - не будет желания , меняйте провайдера
В MediaWiki данная проблема решена давным-давно. Просто есть список адресов, для которых разрешен x-forwarder-for, доверительные прокси, так сказать. Правда его правильная настройка уже на совести администратора конкретного сайта.
Во многих разработках пользователи просят дописать такую возможность и в половине случаев не сразу вспоминаешь что заголовок этот не безопасен.