X

PHP: Как определить IP адрес пользователя?

Сегодня поговорим о том, как определить IP адрес пользователя в PHP, как его проверить с помощью регулярных выражений, как его конвертировать в число и обратно, как его хранить в базе данных MySQL, а так же о том, какие ip адреса заранее зарезервированы..

Вступление

IP адрес - это сокращение от Internet Protocol Address. Это числовой идентификатор компьютера или устройства в локальной сети или сети интернет. Бывают двух версий:

  • IPv4 (32 бита), пример: 192.168.0.1
  • IPv6 (128 бит), пример: 0000:0000:0000:0000:0000:0000:0000:1 или ::1

В этой статье мы поговорим про ip-адреса версии IPv4, т.е. об ip адресах из диапазона: 0.0.0.0 - 255.255.255.255

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

IP адрес пользователя, в PHP, передается в глобальном массиве $_SERVER. Получить его можно использовав ключ массива REMOTE_ADDR, пример:

<?php

$ip = $_SERVER['REMOTE_ADDR'];
echo $ip;

В результате выполнения скрипта, вы получите ip адрес посетителя.

Какой адрес будет показан при входе с моего ПК, на веб-сервер на моем ПК?

В случае, если Вы установили веб-сервер на свой ПК (например, Openserver) и открываете страницу опять же со своего ПК, вы увидите адрес 127.0.0.1. Это нормально, при переносе на нормальный хостинг, будет показан Ваш внешний IP.

Что будет при попытке получить ip-адрес выполнив скрипт из консоли?

Если Вы попробуете запустить такой скрипт из консоли, например создав файл ip.php с содержимым:

<?php
error_reporting(-1);
$ip = $_SERVER['REMOTE_ADDR'];
echo $ip;

и затем в консоли выполните:

# php ip.php

то вы получите ошибку, в которой будет написано, что PHP не может найти значение по указанному ключу

Notice: Undefined index: REMOTE_ADDR in ip.php on line 3

Дело в том, что эти переменные устанавливает веб-сервер и при запуске "напрямую" они не будут установлены

Как проверить откуда запускается скрипт из консоли или через веб-сервер?

Проверить откуда запускается скрипт можно с помощью функции php_sapi_name()

<?php
$ip = null;
if ((php_sapi_name() != 'cli')) {
    $ip = $_SERVER['REMOTE_ADDR'];
}

Более надежными способом будет дополнительная проверка на существование ключа массива, вот так:

<?php
$ip = null;
if ((php_sapi_name() != 'cli') && isset($SERVER['REMOTE_ADDR'])) {
    $ip = $_SERVER['REMOTE_ADDR'];
}

Если у Вас возникнет вопрос, почему просто не проверить через isset, отвечаю: переменная REMOTE_ADDR может быть установлена с помощью переменных окружения, тогда проверка isset пройдет, но значение может не соответствовать действительности. Да, можно проигнорировать проверку с помощью php_sapi_name(), но вы должны отдавать себе отчет в том, что вы делаете и почему именно так.

Как проверить IP адрес с помощью регулярного выражения?

Проверить IP адрес с помощью регулярного выражения можно простой проверкой:

$ip = $_SERVER['REMOTE_ADDR'];

if (preg_match('~^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$~', $ip)) {
    echo 'Корректный';
} else {
    echo 'Не корректный';
}

Здесь проверяется, что в $ip содержится такая строка:

(1-3 цифры от 0 до 9) точка (1-3 цифры от 0 до 9) точка (1-3 цифры от 0 до 9) точка (1-3 цифры от 0 до 9)

Более точной будет проверка ip-адреса вот таким выражением:

$ip = $_SERVER['REMOTE_ADDR'];

if (preg_match('~^(25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[0-9]{2}|[0-9])(\.(25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[0-9]{2}|[0-9])){3}$~', $ip)) {
    echo 'Корректный';
} else {
    echo 'Не корректный';
}

Здесь проверяется, что в $ip содержится такая строка:

(цифра от 0 до 255) точка (цифра от 0 до 255) точка (цифра от 0 до 255) точка (цифра от 0 до 255)

Как определить IP адрес посетителя, находящегося за прокси сервером?

Достоверно на 100% определить нельзя. Можно попытать удачи с переменными HTTP_CLIENT_IP и HTTP_X_FORWARDED_FOR, но надо помнить что все переменные массива $_SERVER начинающиеся на HTTP_* это заголовки запроса, которые могут быть подделаны, переопределены, полностью отсутствовать, иметь не верный формат или содержать какую-нибудь гадость, например, sql-инъекцию.

Обычно используют так:

$ip = null;

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

echo $ip

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

<?php
error_reporting(-1);

$ip = null;

if (php_sapi_name() != 'cli') {
  if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
      $ip = $_SERVER['HTTP_CLIENT_IP'];
  } elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
      $ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
  } elseif(!empty($_SERVER['REMOTE_ADDR'])) {
      $ip = $_SERVER['REMOTE_ADDR'];
  }

  if (!preg_match('~^(25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[0-9]{2}|[0-9])(\.(25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[0-9]{2}|[0-9])){3}$~', $ip)) {
    $ip = null;
  }
}

echo $ip

Как хранить IP адрес в базе данных MySQL?

Лично я предпочитаю хранить IP адрес в MySQL в поле типа VARCHAR(15), по длине самого длинного IPv4 адреса = 255.255.255.255

Другой способ, это привести IP адрес в к типу UNSIGNED INT. Это позволяет экономить память: 4 байта у UNSIGNED INT против 15 у CHAR.

Преобразовать IPv4 адрес к типу INT в PHP можно так

// ip адрес в число
echo ip2long('127.0.0.1');
> 2130706433

// число в ip адрес
long2ip('2130706433');
> 127.0.0.1

Тоже самое можно сделать и на стороне MySQL, для этого надо использовать функции INET_ATON() и INET_NTOA()

Запомнить эти функции достаточно просто:
ATON = Address TO Numaber
NTOA = Number TO Adress

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

# ip адрес в число
SELECT INET_ATON('127.0.0.1');
> 2130706433

# число в ip-адрес
SELECT INET_ATON('2130706433');
> 127.0.0.1

Выбор, что использовать для хранения VARCHAR или INT стоит за проектировщиком БД и удобством использования в будущем.

Какие IP адреса являются предопределенными?

Для локальных сетей предопределены такие диапазоны IP адресов:

  • 10.0.0.0/8 или 10.0.0.0 - 10.255.255.255
  • 172.16.0.0/12 или 172.16.0.0 - 172.31.255.255
  • 192.168.0.0/16 или 192.168.0.0 - 192.168.255.255

Для коммуникаций внутри хоста (между программами запущенными на вашем ПК) используется диапазон ip-адресов: 127.0.0.0/8 или 127.0.0.0 - 127.255.255.255 .

Блок с 169.254.1.0 по 169.254.254.255 (подсеть 169.254.0.0/16 за исключением подсетей 169.254.0.0/24 и 169.254.255.0/24) —
используется для автоматической настройки сетевого интерфейса в случае отсутствия DHCP

 

Категории: MySQL PHP