Какой самый точный способ получить правильный IP-адрес пользователя в PHP? - PullRequest
267 голосов
/ 28 октября 2009

Я знаю, что существует множество $ _ 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 и не беспокойтесь о тех, кто стоит за прокси.

Ответы [ 16 ]

0 голосов
/ 17 июня 2014

Просто еще один чистый способ:

  function validateIp($var_ip){
    $ip = trim($var_ip);

    return (!empty($ip) &&
            $ip != '::1' &&
            $ip != '127.0.0.1' &&
            filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false)
            ? $ip : false;
  }

  function getClientIp() {
    $ip = @$this->validateIp($_SERVER['HTTP_CLIENT_IP']) ?:
          @$this->validateIp($_SERVER['HTTP_X_FORWARDED_FOR']) ?:
          @$this->validateIp($_SERVER['HTTP_X_FORWARDED']) ?:
          @$this->validateIp($_SERVER['HTTP_FORWARDED_FOR']) ?:
          @$this->validateIp($_SERVER['HTTP_FORWARDED']) ?:
          @$this->validateIp($_SERVER['REMOTE_ADDR']) ?:
          'LOCAL OR UNKNOWN ACCESS';

    return $ip;
  }
0 голосов
/ 19 июня 2013

Вот модифицированная версия, если вы используете CloudFlare Служба кэширования слоя

function getIP()
{
    $fields = array('HTTP_X_FORWARDED_FOR',
                    'REMOTE_ADDR',
                    'HTTP_CF_CONNECTING_IP',
                    'HTTP_X_CLUSTER_CLIENT_IP');

    foreach($fields as $f)
    {
        $tries = $_SERVER[$f];
        if (empty($tries))
            continue;
        $tries = explode(',',$tries);
        foreach($tries as $try)
        {
            $r = filter_var($try,
                            FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 |
                            FILTER_FLAG_NO_PRIV_RANGE |
                            FILTER_FLAG_NO_RES_RANGE);

            if ($r !== false)
            {
                return $try;
            }
        }
    }
    return false;
}
0 голосов
/ 25 мая 2012

Спасибо за это, очень полезно.

Это помогло бы, если бы код был синтаксически правильным. Так как это есть {слишком много вокруг строки 20. Что, боюсь, означает, что на самом деле никто не пробовал это.

Возможно, я схожу с ума, но, попробовав его на нескольких допустимых и недействительных адресах, единственная сработавшая версия validate_ip () была такова:

    public function validate_ip($ip)
    {
        if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE) === false)
            return false;
        if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_RES_RANGE) === false)
            return false;
        if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) === false && filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false)
            return false;

        return true;
    }
0 голосов
/ 15 января 2010

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

0 голосов
/ 09 января 2010
/**
 * Sanitizes IPv4 address according to Ilia Alshanetsky's book
 * "php|architect?s Guide to PHP Security", chapter 2, page 67.
 *
 * @param string $ip An IPv4 address
 */
public static function sanitizeIpAddress($ip = '')
{
if ($ip == '')
    {
    $rtnStr = '0.0.0.0';
    }
else
    {
    $rtnStr = long2ip(ip2long($ip));
    }

return $rtnStr;
}

//---------------------------------------------------

/**
 * Returns the sanitized HTTP_X_FORWARDED_FOR server variable.
 *
 */
public static function getXForwardedFor()
{
if (isset($_SERVER['HTTP_X_FORWARDED_FOR']))
    {
    $rtnStr = $_SERVER['HTTP_X_FORWARDED_FOR'];
    }
elseif (isset($HTTP_SERVER_VARS['HTTP_X_FORWARDED_FOR']))
    {
    $rtnStr = $HTTP_SERVER_VARS['HTTP_X_FORWARDED_FOR'];
    }
elseif (getenv('HTTP_X_FORWARDED_FOR'))
    {
    $rtnStr = getenv('HTTP_X_FORWARDED_FOR');
    }
else
    {
    $rtnStr = '';
    }

// Sanitize IPv4 address (Ilia Alshanetsky):
if ($rtnStr != '')
    {
    $rtnStr = explode(', ', $rtnStr);
    $rtnStr = self::sanitizeIpAddress($rtnStr[0]);
    }

return $rtnStr;
}

//---------------------------------------------------

/**
 * Returns the sanitized REMOTE_ADDR server variable.
 *
 */
public static function getRemoteAddr()
{
if (isset($_SERVER['REMOTE_ADDR']))
    {
    $rtnStr = $_SERVER['REMOTE_ADDR'];
    }
elseif (isset($HTTP_SERVER_VARS['REMOTE_ADDR']))
    {
    $rtnStr = $HTTP_SERVER_VARS['REMOTE_ADDR'];
    }
elseif (getenv('REMOTE_ADDR'))
    {
    $rtnStr = getenv('REMOTE_ADDR');
    }
else
    {
    $rtnStr = '';
    }

// Sanitize IPv4 address (Ilia Alshanetsky):
if ($rtnStr != '')
    {
    $rtnStr = explode(', ', $rtnStr);
    $rtnStr = self::sanitizeIpAddress($rtnStr[0]);
    }

return $rtnStr;
}

//---------------------------------------------------

/**
 * Returns the sanitized remote user and proxy IP addresses.
 *
 */
public static function getIpAndProxy()
{
$xForwarded = self::getXForwardedFor();
$remoteAddr = self::getRemoteAddr();

if ($xForwarded != '')
    {
    $ip    = $xForwarded;
    $proxy = $remoteAddr;
    }
else
    {
    $ip    = $remoteAddr;
    $proxy = '';
    }

return array($ip, $proxy);
}
0 голосов
/ 28 октября 2009

Вы в значительной степени ответили на свой вопрос! :)

function getRealIpAddr() {
    if(!empty($_SERVER['HTTP_CLIENT_IP']))   //Check IP address from shared Internet
    {
        $IPaddress = $_SERVER['HTTP_CLIENT_IP'];
    }
    elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR']))   //To check IP address is passed from the proxy
    {
        $IPaddress = $_SERVER['HTTP_X_FORWARDED_FOR'];
    }
    else
    {
        $IPaddress = $_SERVER['REMOTE_ADDR'];
    }
    return $IPaddress;
}

Источник

...