Как получить IP-адрес клиента в PHP
Как мне получить IP-адрес клиента с помощью PHP?
Я хочу зафиксировать пользователя, который зашел на мой сайт, по его IP-адресу.
5 ответ(ов)
$_SERVER['REMOTE_ADDR']
может не содержать реальные IP-адреса клиентов, особенно если они подключены через прокси. В таком случае, вы получите IP-адрес прокси-сервера, что может быть именно тем, что вам нужно, в зависимости от вашей задачи с IP-адресами. Например, частный адрес RFC1918 не будет полезен, если вы хотите узнать, откуда исходит ваш трафик, или запомнить, с какого IP-адреса пользователь подключался в последний раз. В этих случаях более уместным будет сохранить публичный IP-адрес прокси или NAT-шлюза.
Существуют различные HTTP-заголовки, такие как X-Forwarded-For
, которые могут быть установлены различными прокси-серверами. Проблема заключается в том, что это всего лишь HTTP-заголовки, которые может установить кто угодно. Гарантии о содержимом этих заголовков нет. $_SERVER['REMOTE_ADDR']
— это действительный физический IP-адрес, с которого веб-сервер получил соединение и на который будет отправлен ответ. Все остальные данные являются произвольной и добровольной информацией. Вы можете доверять этим данным только в одном случае: если вы контролируете прокси-сервер, который устанавливает этот заголовок. То есть, следует полагаться на эту информацию только если вы на 100% уверены в том, где и как был установлен заголовок.
Тем не менее, вот пример кода:
if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
$ip = $_SERVER['HTTP_CLIENT_IP'];
} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
} else {
$ip = $_SERVER['REMOTE_ADDR'];
}
Примечание редактора: Использование приведенного выше кода имеет потенциальные проблемы с безопасностью. Клиент может установить любую информацию в HTTP-заголовках (т.е. $_SERVER['HTTP_...
) на произвольные значения. Поэтому гораздо надежнее использовать $_SERVER['REMOTE_ADDR']
, поскольку это значение не может быть установлено пользователем.
В PHP для получения IP-адреса клиента вы можете использовать суперглобальный массив $_SERVER
. Вот пример кода:
echo $_SERVER['REMOTE_ADDR'];
$_SERVER['REMOTE_ADDR']
возвращает IP-адрес пользователя, отправившего запрос к вашему серверу. Это полезно для различных задач, таких как ведение логов, ограничение доступа и другие функциональности, связанные с безопасностью.
Если вам нужно быть более уверенным в получаемом IP, особенно если ваш сервер находится за прокси или балансировщиком нагрузки, вы можете рассмотреть наличие других переменных, таких как HTTP_X_FORWARDED_FOR
, которая может содержать оригинальный адрес клиента:
function getUserIP() {
if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
// Если есть несколько IP через запятую, берем первый
$ip = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'])[0];
} else {
$ip = $_SERVER['REMOTE_ADDR'];
}
return $ip;
}
echo getUserIP();
Этот подход позволит вам корректно определить IP-адрес клиента в более сложных сетевых конфигурациях. Имейте в виду, что HTTP_X_FORWARDED_FOR
может быть подделан, поэтому следует использовать этот метод с осторожностью.
Вот более чистый пример кода, как получить IP-адрес пользователя:
$ip = $_SERVER['HTTP_CLIENT_IP']
? $_SERVER['HTTP_CLIENT_IP']
: ($_SERVER['HTTP_X_FORWARDED_FOR']
? $_SERVER['HTTP_X_FORWARDED_FOR']
: $_SERVER['REMOTE_ADDR']);
А вот более короткая версия, использующая оператор Эльвис:
$ip = $_SERVER['HTTP_CLIENT_IP']
? : ($_SERVER['HTTP_X_FORWARDED_FOR']
? : $_SERVER['REMOTE_ADDR']);
Также есть версия, использующая isset
, чтобы избежать предупреждений (спасибо, @shasi kanth):
$ip = isset($_SERVER['HTTP_CLIENT_IP'])
? $_SERVER['HTTP_CLIENT_IP']
: (isset($_SERVER['HTTP_X_FORWARDED_FOR'])
? $_SERVER['HTTP_X_FORWARDED_FOR']
: $_SERVER['REMOTE_ADDR']);
Этот код поможет вам корректно получить IP-адрес пользователя, учитывая различные сценарии.
Эта информация должна содержаться в переменной $_SERVER['REMOTE_ADDR']
.
Мой любимый подход реализован в Zend Framework 2. Этот фреймворк учитывает свойства $_SERVER
, такие как HTTP_X_FORWARDED_FOR
, HTTP_CLIENT_IP
, REMOTE_ADDR
, но при этом создает класс, позволяющий определить надежные прокси. Он возвращает один IP-адрес, а не массив, что, на мой взгляд, является наиболее правильным решением.
Вот пример реализации:
class RemoteAddress
{
/**
* Использовать ли адреса прокси или нет.
*
* По умолчанию этот параметр отключен — IP-адрес в основном необходим для повышения
* безопасности. HTTP_* ненадежны, так как их можно легко подделать. Этот параметр можно включить
* для большей гибкости, но если пользователь использует прокси для подключения к доверенным услугам,
* это его личный риск. Единственным надежным полем для IP-адреса является $_SERVER['REMOTE_ADDR'].
*
* @var bool
*/
protected $useProxy = false;
/**
* Список доверенных IP-адресов прокси
*
* @var array
*/
protected $trustedProxies = array();
/**
* HTTP-заголовок для анализа на предмет прокси
*
* @var string
*/
protected $proxyHeader = 'HTTP_X_FORWARDED_FOR';
// [...]
/**
* Возвращает IP-адрес клиента.
*
* @return string IP-адрес.
*/
public function getIpAddress()
{
$ip = $this->getIpAddressFromProxy();
if ($ip) {
return $ip;
}
// прямой IP-адрес
if (isset($_SERVER['REMOTE_ADDR'])) {
return $_SERVER['REMOTE_ADDR'];
}
return '';
}
/**
* Пытается получить IP-адрес для проксированного клиента
*
* @see http://tools.ietf.org/html/draft-ietf-appsawg-http-forwarded-10#section-5.2
* @return false|string
*/
protected function getIpAddressFromProxy()
{
if (!$this->useProxy
|| (isset($_SERVER['REMOTE_ADDR']) && !in_array($_SERVER['REMOTE_ADDR'], $this->trustedProxies))
) {
return false;
}
$header = $this->proxyHeader;
if (!isset($_SERVER[$header]) || empty($_SERVER[$header])) {
return false;
}
// Извлечение IP-адресов
$ips = explode(',', $_SERVER[$header]);
// обрезка, чтобы корректно сравнивать с доверенными прокси
$ips = array_map('trim', $ips);
// удаление доверенных IP-прокси
$ips = array_diff($ips, $this->trustedProxies);
// Остались хоть какие-то?
if (empty($ips)) {
return false;
}
// Поскольку мы удалили любые известные, доверенные прокси, крайний правый
// адрес представляет собой первый IP, который мы не знаем — т.е. мы не знаем,
// является ли он прокси-сервером или клиентом. Следовательно, мы рассматриваем его
// как исходный IP.
// @see http://en.wikipedia.org/wiki/X-Forwarded-For
$ip = array_pop($ips);
return $ip;
}
// [...]
}
Полный код можно найти здесь: https://raw.githubusercontent.com/zendframework/zend-http/master/src/PhpEnvironment/RemoteAddress.php
Как вывести ошибки PHP на экран?
UTF-8 на всех уровнях!
Функции startsWith() и endsWith() в PHP
Установить переменные окружения из файла с парами ключ/значение
Что такое потокобезопасность и непотокобезопасность в PHP?