Артём Мáлков

Как получить IP-адрес посетителя сайта в PHP?

18 мар0 комм

Если вы пишите свой модуль статистики, боретесь с подозрительной активностью, например, через блокировку доступа к вашему ресурсу или решаете другую подобную задачу, IP-адрес – это тот параметр, за который можно «зацепиться», ведь он позволяет в какой-то степени отличить одного посетителя сайта от другого.

За свою скромную карьеру программиста я сталкивался с этим вопросом много раз, видел разные реализации описанного и на личном опыте, открыл для себя некоторые моменты, о которых сегодня расскажу в этом посте.

Итак, простое получение IP-адреса пользователя в PHP осуществляется через суперглобальный массив $_SERVER с ключом REMOTE_ADDR:

echo $_SERVER['REMOTE_ADDR'];

Вполне рабочий вариант, но в случае, когда человек использует прокси, REMOTE_ADDR вернет нам не его (человека) реальный IP-адрес, а конечный этого прокси-сервера, через который он попал на сайт. Чтобы учесть это, воспользуйтесь следующей функцией:

function getUserIP() {

	if(!empty($_SERVER['HTTP_CLIENT_IP'])) {
	
		$userIP = $_SERVER['HTTP_CLIENT_IP'];
		
	} elseif(!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
	
		$userIP = $_SERVER['HTTP_X_FORWARDED_FOR'];
		
	} else {
	
		$userIP = $_SERVER['REMOTE_ADDR'];
		
	}

	return $userIP;
	
}

Использование:

echo getUserIP();

Это (в общем) итоговый вариант, а теперь к нюансам.

1. Иногда нам может вернуться не один IP-адрес, а список из нескольких. Можете оставить как есть, это просто уточнение для вас, а можете получить один, например, последний:

$userIP = explode(',', $_SERVER['REMOTE_ADDR']); // Разбиваем список IP-адресов на массив 

echo trim(end($userIP)); // Выводим последний элемент (IP-адрес) и удаляем у него пробелы по бокам

В случае с функцией выше эту конструкцию можно написать вместо строчки с return:

$userIP = explode(',', $userIP);
	
return trim(end($userIP));

2. По-хорошему, особенно если вы работаете с базой данных, полученный результат лучше фильтровать (об этом ниже).

3. Если ваш сайт подключен к Cloudflare, то стоит проверять IP, который он возвращает:

if(isset($_SERVER['HTTP_CF_CONNECTING_IP'])) { // Если сайт подключен к Cloudflare
	
	echo $_SERVER['HTTP_CF_CONNECTING_IP'];

} else {

	echo $_SERVER['REMOTE_ADDR'];
	
}

Выше представленная функция тогда будет иметь следующий вид:

function getUserIP() {

	if(!empty($_SERVER['HTTP_CLIENT_IP'])) {
	
		$userIP = $_SERVER['HTTP_CLIENT_IP'];
		
	} elseif(!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
	
		$userIP = $_SERVER['HTTP_X_FORWARDED_FOR'];
		
	} elseif(isset($_SERVER['HTTP_CF_CONNECTING_IP'])) { // Если сайт подключен к Cloudflare
	
		$userIP = $_SERVER['HTTP_CF_CONNECTING_IP'];
			
	} else {
	
		$userIP = $_SERVER['REMOTE_ADDR'];
		
	}
	
	return $userIP;
	
}

Когда я писал свой модуль статистики, именно этот момент «всплыл» у некоторых пользователей, которые начали его использовать.

Фильтрация (валидация) IP-адресов в PHP

Раз уж зашёл разговор о получении IP-адресов, то уместно сделать акцент и на их обработке. В последнее время для себя я взял за правило, что любые данные, которые я получаю извне, нужно фильтровать, поскольку это вопрос не столько правильности работы скрипта, сколько вопрос безопасности.

В PHP на такой случай (фильтрация IP) существует функция filter_var.

Простой пример использования:

$userIP = filter_var($_SERVER['REMOTE_ADDR'], FILTER_VALIDATE_IP);

if($userIP) {
	
	echo $userIP;
	
} else {
	
	echo 'IP-адрес невалиден';
	
}

В ранее написанной нами функции её можно использовать вместо строчки с return:

$userIP = filter_var($userIP, FILTER_VALIDATE_IP);

if($userIP) {

	return $userIP;

} else {

	return 'IP-адрес невалиден';

}

Это общий вариант использования функции, который проверяет корректность IP-адреса (v4 или v6). Дополнительно можно использовать флаги:

  1. FILTER_FLAG_IPV4 – IP-адрес формата IPv4;
  2. FILTER_FLAG_IPV6 – IP-адрес формата IPv6;
  3. FILTER_FLAG_NO_PRIV_RANGE – IP-адрес не входит в диапазон частных адресов;
  4. FILTER_FLAG_NO_RES_RANGE – IP-адрес не входит в диапазон зарезервированных адресов.

Пример использования:

$userIP = filter_var($_SERVER['REMOTE_ADDR'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4);

if($userIP) {
	
	echo $userIP;
	
} else {
	
	echo 'IP-адрес невалиден';
	
}

Флаги можно совмещать с помощью побитового оператора | (операция ИЛИ):

$userIP = filter_var($_SERVER['REMOTE_ADDR'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE);

if($userIP) {
	
	echo $userIP;
	
} else {
	
	echo 'IP-адрес невалиден';
	
}

Таким образом, мы проверяем, что IP-адрес имеет формат IPv4 и он не входит в диапазон частных адресов.

А вот FILTER_FLAG_IPV4 и FILTER_FLAG_IPV6 между собой совмещать не стоит, поскольку функция это проверяет по умолчанию.

Рекомендуем к просмотру
Получение местоположения (геопозиции) пользователя по IP-адресу на PHP+API
Посты
Как определить, что посетитель вашего сайта поисковый робот, а не человек на PHP?
Посты
Как отобразить IP-адреса посетителей в «Яндекс.Метрике» на PHP?
Хаки
0
комментариев
Форма комментирования этого поста скрыта. Авторизуйтесь, чтобы расширить привилегии гостевого посещения и получить необходимую помощь от сообщества Pandoge.
Подняться наверх
«Pandoge» - помощник вебмастера