Я знаю, что есть множество $ _SERVER для получения IP-адреса доступно заголовков переменных . Мне было интересно, существует ли общее согласие относительно того, как наиболее точно получить реальный IP-адрес пользователя (хорошо зная, что ни один метод не идеален), используя указанные переменные?
Я потратил некоторое время, пытаясь найти глубокое решение, и придумал следующий код, основанный на нескольких источниках. Я был бы рад, если бы кто-нибудь мог просочиться в ответе или пролить свет на что-то более точное.
редактирование включает в себя оптимизации от @Alix
/**
* Retrieves the best guess of the client's actual IP address.
* Takes into account numerous HTTP proxy headers due to variations
* in how different ISPs handle IP addresses in headers between hops.
*/
public function get_ip_address() {
// Check for shared internet/ISP IP
if (!empty($_SERVER['HTTP_CLIENT_IP']) && $this->validate_ip($_SERVER['HTTP_CLIENT_IP']))
return $_SERVER['HTTP_CLIENT_IP'];
// Check for IPs passing through proxies
if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
// Check if multiple IP addresses exist in var
$iplist = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
foreach ($iplist as $ip) {
if ($this->validate_ip($ip))
return $ip;
}
}
}
if (!empty($_SERVER['HTTP_X_FORWARDED']) && $this->validate_ip($_SERVER['HTTP_X_FORWARDED']))
return $_SERVER['HTTP_X_FORWARDED'];
if (!empty($_SERVER['HTTP_X_CLUSTER_CLIENT_IP']) && $this->validate_ip($_SERVER['HTTP_X_CLUSTER_CLIENT_IP']))
return $_SERVER['HTTP_X_CLUSTER_CLIENT_IP'];
if (!empty($_SERVER['HTTP_FORWARDED_FOR']) && $this->validate_ip($_SERVER['HTTP_FORWARDED_FOR']))
return $_SERVER['HTTP_FORWARDED_FOR'];
if (!empty($_SERVER['HTTP_FORWARDED']) && $this->validate_ip($_SERVER['HTTP_FORWARDED']))
return $_SERVER['HTTP_FORWARDED'];
// Return unreliable IP address since all else failed
return $_SERVER['REMOTE_ADDR'];
}
/**
* Ensures an IP address is both a valid IP address and does not fall within
* a private network range.
*
* @access public
* @param string $ip
*/
public function validate_ip($ip) {
if (filter_var($ip, FILTER_VALIDATE_IP,
FILTER_FLAG_IPV4 |
FILTER_FLAG_IPV6 |
FILTER_FLAG_NO_PRIV_RANGE |
FILTER_FLAG_NO_RES_RANGE) === false)
return false;
self::$ip = $ip;
return true;
}
Слова предупреждения (обновление)
REMOTE_ADDR
по-прежнему представляет собой наиболее надежный источник IP-адреса. Другие $_SERVER
переменные, упомянутые здесь, могут быть легко подделаны удаленным клиентом. Цель этого решения - попытаться определить IP-адрес клиента, сидящего за прокси-сервером. В общих целях вы можете рассмотреть возможность использования этого в сочетании с IP-адресом, возвращенным непосредственно из$_SERVER['REMOTE_ADDR']
и хранением обоих.
Для 99,9% пользователей это решение идеально подойдет вашим потребностям. Это не защитит вас от 0,1% злонамеренных пользователей, которые хотят злоупотреблять вашей системой, вводя свои собственные заголовки запросов. Если вы полагаетесь на IP-адреса для чего-то критически важного, прибегайте к REMOTE_ADDR
услугам тех, кто стоит за прокси-сервером.
источник
Ответы:
Вот более короткий и понятный способ получить IP-адрес:
Я надеюсь, что это помогает!
Ваш код, кажется, уже достаточно завершен, я не вижу никаких возможных ошибок в нем (кроме обычных предупреждений IP),
validate_ip()
хотя я бы изменил функцию, полагаясь на расширение фильтра:Также ваш
HTTP_X_FORWARDED_FOR
фрагмент может быть упрощен из этого:К этому:
Вы также можете проверить IPv6-адреса.
источник
filter_var
исправление, поскольку оно удаляет кучу взломанных проверок без знака на IP-адресе. Мне также нравится тот факт, что он дает мне возможность проверки адресов IPv6.HTTP_X_FORWARDED_FOR
Оптимизация также высоко ценится. Через несколько минут я обновлю код.Однако даже в этом случае получение реального IP-адреса пользователя будет ненадежным. Все, что им нужно сделать, это использовать анонимный прокси-сервер (тот, который не учитывает заголовки для http_x_forwarded_for, http_forwarded и т. Д.), И все, что вы получаете, это IP-адрес их прокси-сервера.
Затем вы можете увидеть, есть ли список IP-адресов прокси-серверов, которые являются анонимными, но нет никакого способа убедиться, что он также точен на 100%, и самое большее, что он может сделать, это сообщить вам, что это прокси-сервер. А если кто-то умен, он может подделать заголовки для пересылки HTTP.
Допустим, мне не нравится местный колледж. Я выясняю, какие IP-адреса они зарегистрировали, и блокирую их IP-адреса на вашем сайте, делая плохие вещи, потому что я выясняю, что вы соблюдаете правила HTTP. Список бесконечен.
Кроме того, как вы уже догадались, существуют внутренние IP-адреса, такие как сеть колледжа, о которой я упоминал ранее. Многие используют формат 10.xxx. Так что все, что вы знаете, это то, что он был перенаправлен в общую сеть.
Тогда я не буду в этом начинать, но динамические IP-адреса больше не нужны. Так. Даже если вы получите IP-адрес пользователя, ожидайте, что он изменится через 2-3 месяца, самое большее.
источник
Мы используем:
Разрыв HTTP_X_FORWARDED_FOR из-за странных проблем, с которыми мы столкнулись при обнаружении IP-адресов, когда Squid .
источник
Мой ответ, в основном, является просто отлаженной, полностью проверенной и полностью упакованной версией ответа @ AlixAxel:
Изменения:
Это упрощает имя функции (с использованием стиля форматирования 'camelCase').
Он включает проверку, чтобы убедиться, что функция еще не объявлена в другой части вашего кода.
Он учитывает совместимость с CloudFlare.
Он инициализирует несколько «связанных с IP» имен переменных к возвращаемому значению функции getClientIP.
Это гарантирует, что если функция не возвращает действительный IP-адрес, все переменные будут установлены в пустую строку вместо
null
.Это всего лишь (45) строк кода.
источник
Самый большой вопрос для чего?
Ваш код почти настолько всеобъемлющий, насколько это возможно - но я вижу, что если вы заметите то, что выглядит как добавленный заголовок прокси, вы используете этот INSTEAD из CLIENT_IP, однако, если вы хотите, чтобы эта информация использовалась в целях аудита, вы должны быть предупреждены - это очень легко подделать.
Конечно, вы никогда не должны использовать IP-адреса для какой-либо аутентификации - даже они могут быть подделаны.
Вы могли бы лучше измерить IP-адрес клиента, выдвинув флэш- или Java-апплет, который подключается к серверу через порт, отличный от http (что, следовательно, выявляет прозрачные прокси-серверы или случаи, когда прокси-инъецированные заголовки ложны - но имейте в виду, что если клиент может подключиться ТОЛЬКО через веб-прокси или исходящий порт заблокирован, соединение с апплетом не будет.
C.
источник
$_SERVER['CLIENT_IP']
в качестве второго оператора if?я понимаю, что есть намного лучшие и более краткие ответы выше, и это не функция, ни самый изящный сценарий. В нашем случае нам нужно было вывести как поддельный x_forwarded_for, так и более надежный remote_addr в упрощенном ключе. Нужно было разрешить вводить пробелы в другие функции if-none или if-singular (а не просто возвращать предварительно отформатированную функцию). Требовалась переменная «вкл. Или выкл.» С индивидуальной меткой (ами) для настроек платформы. Также необходимо, чтобы $ ip был динамическим в зависимости от запроса, чтобы он принял форму forwarded_for.
Также я не видел никого из адресов isset () против! Empty () - можно ничего не вводить для x_forwarded_for, но все еще вызывать истину isset (), приводящую к пустым переменным, способ обойти это использовать && и объединить оба в качестве условий. Имейте в виду, что вы можете подделать такие слова, как «PWNED», как x_forwarded_for, поэтому убедитесь, что вы стерилизуетесь по реальному синтаксису ip, если ваш вывод где-то защищен или в БД.
Также вы можете протестировать с помощью Google Translate, если вам нужен мульти-прокси, чтобы увидеть массив в x_forwarder_for. Если вы хотите проверить поддельные заголовки, проверьте это в Chrome Client Header Spoof расширение . Это будет по умолчанию просто стандартный remote_addr, пока находится за прокси-сервером.
Я не знаю ни одного случая, когда remote_addr мог бы быть пустым, но его можно использовать как запасной вариант на всякий случай.
Чтобы сделать их динамическими для использования в функциях (функциях) или запросах / эхо / представлениях ниже, скажем, для регистрации событий или отчетов об ошибках, используйте глобальные переменные или просто выводите их в любом месте, где вы хотите, не создавая кучу других условий или статических схемных выходов. функции.
Спасибо за все ваши великие мысли. Пожалуйста, дайте мне знать, если это могло бы быть лучше, все еще немного нового для этих заголовков :)
источник
Я придумал эту функцию, которая не просто возвращает IP-адрес, но и массив с IP-информацией.
Вот функция:
источник
Как кто-то сказал ранее, ключ здесь заключается в том, по какой причине вы хотите хранить ips пользователя.
Я приведу пример из системы регистрации, над которой я работаю, и, конечно, решение только для того, чтобы внести свой вклад в это старое обсуждение, которое часто встречается в моих поисках.
Многие библиотеки регистрации php используют ip для ограничения / блокировки неудачных попыток на основе ip пользователя. Рассмотрим эту таблицу:
Затем, когда пользователь пытается выполнить вход в систему или что-либо связанное с обслуживанием, например сброс пароля, в начале вызывается функция:
Скажем, к примеру,
$this->token->get('attempts_before_ban') === 10
и 2 пользователя приходят с теми же ips, что и в предыдущих кодах, где заголовки могут быть подделаны , после 5 попыток каждый из них забанен ! Даже хуже всего, если все они приходят с одного и того же прокси-сервера, то только первые 10 пользователей будут зарегистрированы, а все остальные будут забанены!Критическим здесь является то, что нам нужен уникальный индекс для таблицы,
attempts
и мы можем получить его из такой комбинации:откуда
jwt_load
берется файл cookie http, соответствующий технологии веб-токенов json, где мы храним только зашифрованные данные, которые должны содержать произвольное / уникальное значение для каждого пользователя. Конечно, запрос должен быть изменен на:"SELECT count(*) FROM {$this->token->get('table_attempts')} WHERE ip = ? AND jwt_load = ?"
и класс должен также инициироватьprivate $jwt
.источник
Мне интересно, возможно, вам следует перебирать развернутый HTTP_X_FORWARDED_FOR в обратном порядке, поскольку мой опыт показывает, что IP-адрес пользователя заканчивается в конце списка, разделенного запятыми, поэтому, начиная с начала заголовка, вы с большей вероятностью получит IP-адрес одного из возвращенных прокси-серверов, который потенциально может по-прежнему разрешать перехват сеанса, так как через этот прокси может проходить много пользователей.
источник
Спасибо за это, очень полезно.
Это помогло бы, если бы код был синтаксически правильным. Так как это есть {слишком много вокруг строки 20. Что, я боюсь, означает, что на самом деле никто не пробовал это.
Возможно, я схожу с ума, но после того, как попробовал его на нескольких действительных и недействительных адресах, единственная работающая версия validate_ip () была такова:
источник
Вот модифицированная версия, если вы используете сервисы кэширования CloudFlare Services
источник
Просто VB.NET версия ответа:
источник
Просто еще один чистый способ:
источник
Из класса запросов Symfony https://github.com/symfony/symfony/blob/1bd125ec4a01220878b3dbc3ec3156b073996af9/src/Symfony/Component/HttpFoundation/Request.php
источник
Я удивлен, что никто не упомянул filter_input, так что ответ Аликс Аксель сжат в одну строку:
источник
Вы в значительной степени ответили на свой вопрос! :)
Источник
источник
источник