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

Я знаю, что имеется множество заголовков переменных $_ SERVER, доступных для поиска по IP-адресу. Мне было интересно, существует ли общий консенсус относительно того, как наиболее точно получить реальный IP-адрес пользователя (хорошо зная, что метод не идеальный) с использованием указанных переменных?

Я потратил некоторое время на поиски углубленного решения и разработал следующий код, основанный на нескольких источниках. Мне было бы очень приятно, если бы кто-то мог пробить дыры в ответ или пролить некоторый свет на что-то, что может быть более точным.

редактирование включает в себя оптимизацию из @Alix

 /**
  * Retrieves the best guess of the client 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 и не беспокойтесь о том, чтобы обслуживать тех, кто находится за прокси.

Ответ 1

Ниже представлен более короткий и чистый способ получить IP-адрес:

function get_ip_address(){
    foreach (array('HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'REMOTE_ADDR') as $key){
        if (array_key_exists($key, $_SERVER) === true){
            foreach (explode(',', $_SERVER[$key]) as $ip){
                $ip = trim($ip); // just to be safe

                if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false){
                    return $ip;
                }
            }
        }
    }
}

Надеюсь, это поможет!


Теперь ваш код выглядит довольно полным, я не вижу в нем каких-либо ошибок (кроме обычных оговорок IP), я бы изменил функцию validate_ip(), чтобы полагаться на расширение фильтра, хотя:

public function validate_ip($ip)
{
    if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false)
    {
        return false;
    }

    self::$ip = sprintf('%u', ip2long($ip)); // you seem to want this

    return true;
}

Также ваш фрагмент HTTP_X_FORWARDED_FOR можно упростить:

// check for IPs passing through proxies
if (!empty($_SERVER['HTTP_X_FORWARDED_FOR']))
{
    // check if multiple ips exist in var
    if (strpos($_SERVER['HTTP_X_FORWARDED_FOR'], ',') !== false)
    {
        $iplist = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);

        foreach ($iplist as $ip)
        {
            if ($this->validate_ip($ip))
                return $ip;
        }
    }

    else
    {
        if ($this->validate_ip($_SERVER['HTTP_X_FORWARDED_FOR']))
            return $_SERVER['HTTP_X_FORWARDED_FOR'];
    }
}

Для этого:

// check for IPs passing through proxies
if (!empty($_SERVER['HTTP_X_FORWARDED_FOR']))
{
    $iplist = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);

    foreach ($iplist as $ip)
    {
        if ($this->validate_ip($ip))
            return $ip;
    }
}

Вы также можете проверить IPv6-адреса.

Ответ 2

Даже тогда, однако, получение реального IP-адреса пользователя будет ненадежным. Все, что им нужно сделать, это использовать анонимный прокси-сервер (тот, который не соблюдает заголовки для http_x_forwarded_for, http_forwarded и т.д.), И все, что вы получаете, это их IP-адрес прокси-сервера.

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

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

Тогда есть, как вы уже догадались, внутренние IP-адреса, такие как сеть колледжа, о которой я уже говорил ранее. Много использовать формат 10.x.x.x. Итак, все, что вы знаете, это то, что он был перенаправлен для общей сети.

Тогда я не буду много начинать с этого, но динамические IP-адреса - это путь широкополосного доступа. Так. Даже если вы получите IP-адрес пользователя, ожидайте, что он изменится через 2 - 3 месяца, дольше всего.

Ответ 3

Мы используем:

/**
 * Get the customer IP address.
 *
 * @return string
 */
public function getIpAddress() {
    if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
        return $_SERVER['HTTP_CLIENT_IP'];
    } else if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
        $ips = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
        return trim($ips[count($ips) - 1]);
    } else {
        return $_SERVER['REMOTE_ADDR'];
    }
}

Взрыв HTTP_X_FORWARDED_FOR вызван странными проблемами, с которыми мы столкнулись при обнаружении IP-адресов при использовании Squid.

Ответ 4

Мой ответ - это просто отполированная, полностью проверенная и полностью упакованная версия ответа @AlixAxel:

<?php

/* Get the 'best known' client IP. */

if (!function_exists('getClientIP'))
    {
        function getClientIP()
            {
                if (isset($_SERVER["HTTP_CF_CONNECTING_IP"])) 
                    {
                        $_SERVER['REMOTE_ADDR'] = $_SERVER["HTTP_CF_CONNECTING_IP"];
                    };

                foreach (array('HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'REMOTE_ADDR') as $key)
                    {
                        if (array_key_exists($key, $_SERVER)) 
                            {
                                foreach (explode(',', $_SERVER[$key]) as $ip)
                                    {
                                        $ip = trim($ip);

                                        if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false)
                                            {
                                                return $ip;
                                            };
                                    };
                            };
                    };

                return false;
            };
    };

$best_known_ip = getClientIP();

if(!empty($best_known_ip))
    {
        $ip = $clients_ip = $client_ip = $client_IP = $best_known_ip;
    }
else
    {
        $ip = $clients_ip = $client_ip = $client_IP = $best_known_ip = '';
    };

?>

Изменения:

  • Это упрощает имя функции (с использованием стиля форматирования "camelCase" ).

  • Он включает проверку, чтобы убедиться, что функция еще не объявлена ​​в другой части вашего кода.

  • Он учитывает совместимость CloudFlare.

  • Инициализирует несколько имен переменных, связанных с IP-адресами, к возвращаемому значению функции getClientIP.

  • Это гарантирует, что если функция не вернет действительный IP-адрес, все переменные будут установлены в пустую строку вместо null.

  • Это только (45) строк кода.

Ответ 5

Самый большой вопрос - с какой целью?

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

Конечно, вы никогда не должны использовать IP-адреса для какой-либо аутентификации - даже они могут быть подделаны.

Вы можете получить лучшее измерение IP-адреса клиента, выталкивая флэш-или java-апплет, который подключается обратно к серверу через порт, отличный от http-порта (поэтому он будет показывать прозрачные прокси-серверы или случаи, когда заголовки с прокси-вводом false - но имейте в виду, что, когда клиент может ТОЛЬКО подключаться через веб-прокси или заблокирован исходящий порт, из апплета не будет никакого соединения.

С.

Ответ 6

Я понимаю, что есть намного лучшие и более сжатые ответы выше, и это не функция и не самый изящный script. В нашем случае нам нужно было вывести как подменю x_forwarded_for, так и более надежный remote_addr в упрощенном режиме. Ему нужно было разрешить пробелы для ввода в другие функции if-none или if-singular (а не просто возврат предварительно отформатированной функции). Для настройки платформы требовалось "включить или выключить" var с индивидуальными метками для каждого коммутатора. Это также потребовало, чтобы $ip был динамическим в зависимости от запроса, чтобы он принял форму forwarded_for.

Также я не видел никого адреса isset() vs! empty() - его можно ничего не вводить для x_forwarded_for, но все же запускать isset() правду, приводящую к пустому var, способ обойти это использовать && & и сочетать как условия. Имейте в виду, что вы можете подделывать слова типа "PWNED" как x_forwarded_for, поэтому убедитесь, что вы стерилизуете реальный синтаксис ip, если вы выходите где-то в защищенном виде или в DB.

Также вы можете протестировать, используя google translate, если вам нужен мультипрокси-сервер, чтобы увидеть массив в x_forwarder_for. Если вы хотите опробовать заголовки для проверки, проверьте это расширение Расширение заголовка клиента Chrome. По умолчанию будет использоваться только стандартный remote_addr, а за другим прокси-сервер.

Я не знаю, в каком случае remote_addr может быть пустым, но его там как резерв на всякий случай.

// proxybuster - attempts to un-hide originating IP if [reverse]proxy provides methods to do so
  $enableProxyBust = true;

if (($enableProxyBust == true) && (isset($_SERVER['REMOTE_ADDR'])) && (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) && (!empty($_SERVER['HTTP_X_FORWARDED_FOR']))) {
    $ip = end(array_values(array_filter(explode(',',$_SERVER['HTTP_X_FORWARDED_FOR']))));
    $ipProxy = $_SERVER['REMOTE_ADDR'];
    $ipProxy_label = ' behind proxy ';
} elseif (($enableProxyBust == true) && (isset($_SERVER['REMOTE_ADDR']))) {
    $ip = $_SERVER['REMOTE_ADDR'];
    $ipProxy = '';
    $ipProxy_label = ' no proxy ';
} elseif (($enableProxyBust == false) && (isset($_SERVER['REMOTE_ADDR']))) {
    $ip = $_SERVER['REMOTE_ADDR'];
    $ipProxy = '';
    $ipProxy_label = '';
} else {
    $ip = '';
    $ipProxy = '';
    $ipProxy_label = '';
}

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

function fooNow() {
    global $ip, $ipProxy, $ipProxy_label;
    // begin this actions such as log, error, query, or report
}

Спасибо за все твои великие мысли. Пожалуйста, дайте мне знать, если это может быть лучше, но все же новичок в этих заголовках:)

Ответ 7

Я придумал эту функцию, которая не просто возвращает IP-адрес, а массив с информацией о IP-адресе.

// Example usage:
$info = ip_info();
if ( $info->proxy ) {
    echo 'Your IP is ' . $info->ip;
} else {
    echo 'Your IP is ' . $info->ip . ' and your proxy is ' . $info->proxy_ip;
}

Здесь функция:

/**
 * Retrieves the best guess of the client actual IP address.
 * Takes into account numerous HTTP proxy headers due to variations
 * in how different ISPs handle IP addresses in headers between hops.
 *
 * @since 1.1.3
 *
 * @return object {
 *         IP Address details
 *
 *         string $ip The users IP address (might be spoofed, if $proxy is true)
 *         bool $proxy True, if a proxy was detected
 *         string $proxy_id The proxy-server IP address
 * }
 */
function ip_info() {
    $result = (object) array(
        'ip' => $_SERVER['REMOTE_ADDR'],
        'proxy' => false,
        'proxy_ip' => '',
    );

    /*
     * This code tries to bypass a proxy and get the actual IP address of
     * the visitor behind the proxy.
     * Warning: These values might be spoofed!
     */
    $ip_fields = array(
        'HTTP_CLIENT_IP',
        'HTTP_X_FORWARDED_FOR',
        'HTTP_X_FORWARDED',
        'HTTP_X_CLUSTER_CLIENT_IP',
        'HTTP_FORWARDED_FOR',
        'HTTP_FORWARDED',
        'REMOTE_ADDR',
    );
    foreach ( $ip_fields as $key ) {
        if ( array_key_exists( $key, $_SERVER ) === true ) {
            foreach ( explode( ',', $_SERVER[$key] ) as $ip ) {
                $ip = trim( $ip );

                if ( filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE ) !== false ) {
                    $forwarded = $ip;
                    break 2;
                }
            }
        }
    }

    // If we found a different IP address then REMOTE_ADDR then it a proxy!
    if ( $forwarded != $result->ip ) {
        $result->proxy = true;
        $result->proxy_ip = $result->ip;
        $result->ip = $forwarded;
    }

    return $result;
}

Ответ 8

Как уже говорилось ранее, ключ здесь по какой причине вы хотите хранить пользователя ips.

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

Многие библиотеки регистрации php используют ip для блокировки/блокировки неудачных попыток на основе пользовательского ip. Рассмотрим эту таблицу:

-- mysql
DROP TABLE IF EXISTS `attempts`;
CREATE TABLE `attempts` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `ip` varchar(39) NOT NULL, /*<<=====*/
  `expiredate` datetime NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 -- sqlite
...

Затем, когда пользователь пытается сделать логин или что-либо, связанное с обслуживанием, например, с паролем reset, в начале запускается функция:

public function isBlocked() {
      /*
       * used one of the above methods to capture user ip!!!
       */
      $ip = $this->ip;
      // delete attempts from this ip with 'expiredate' in the past
      $this->deleteAttempts($ip, false);
      $query = $this->dbh->prepare("SELECT count(*) FROM {$this->token->get('table_attempts')} WHERE ip = ?");
      $query->execute(array($ip));
      $attempts = $query->fetchColumn();
      if ($attempts < intval($this->token->get('attempts_before_verify'))) {
         return "allow";
      }
      if ($attempts < intval($this->token->get('attempts_before_ban'))) {
         return "captcha";
      }
      return "block";
   }

Скажем, например, $this->token->get('attempts_before_ban') === 10 и 2 пользователя для тех же ips, что и в предыдущих кодах , где заголовки могут быть подделаны, а затем по 5 попыток оба запрещен! Даже худшее, если все исходят из одного и того же прокси-сервера, тогда будут регистрироваться только первые 10 пользователей, и все остальные будут запрещены!

Здесь важно, чтобы нам нужен уникальный индекс в таблице attempts, и мы можем получить его из комбинации, например:

 `ip` varchar(39) NOT NULL,
 `jwt_load varchar(100) NOT NULL

где jwt_load происходит из http файла cookie, следующего за технологией json web token, где мы храним только зашифрованныйсильная > полезная нагрузка, которая должна содержать произвольное/уникальное значение для каждого пользователя. Конечно, запрос должен быть изменен на: "SELECT count(*) FROM {$this->token->get('table_attempts')} WHERE ip = ? AND jwt_load = ?", и класс должен также инициировать private $jwt.

Ответ 9

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

Ответ 10

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

Это помогло бы, если бы код был синтаксически правильным. Как там есть {слишком много вокруг линии 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;
    }

Ответ 11

Здесь измененная версия, если вы используете 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;
}

Ответ 12

Просто VB.NET версия ответа:

Private Function GetRequestIpAddress() As IPAddress
    Dim serverVariables = HttpContext.Current.Request.ServerVariables
    Dim headersKeysToCheck = {"HTTP_CLIENT_IP", _
                              "HTTP_X_FORWARDED_FOR", _
                              "HTTP_X_FORWARDED", _
                              "HTTP_X_CLUSTER_CLIENT_IP", _
                              "HTTP_FORWARDED_FOR", _
                              "HTTP_FORWARDED", _
                              "REMOTE_ADDR"}
    For Each thisHeaderKey In headersKeysToCheck
        Dim thisValue = serverVariables.Item(thisHeaderKey)
        If thisValue IsNot Nothing Then
            Dim validAddress As IPAddress = Nothing
            If IPAddress.TryParse(thisValue, validAddress) Then
                Return validAddress
            End If
        End If
    Next
    Return Nothing
End Function

Ответ 13

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

  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;
  }

Ответ 14

Из класса Symfony Request https://github.com/symfony/symfony/blob/1bd125ec4a01220878b3dbc3ec3156b073996af9/src/Symfony/Component/HttpFoundation/Request.php

const HEADER_FORWARDED = 'forwarded';
const HEADER_CLIENT_IP = 'client_ip';
const HEADER_CLIENT_HOST = 'client_host';
const HEADER_CLIENT_PROTO = 'client_proto';
const HEADER_CLIENT_PORT = 'client_port';

/**
 * Names for headers that can be trusted when
 * using trusted proxies.
 *
 * The FORWARDED header is the standard as of rfc7239.
 *
 * The other headers are non-standard, but widely used
 * by popular reverse proxies (like Apache mod_proxy or Amazon EC2).
 */
protected static $trustedHeaders = array(
    self::HEADER_FORWARDED => 'FORWARDED',
    self::HEADER_CLIENT_IP => 'X_FORWARDED_FOR',
    self::HEADER_CLIENT_HOST => 'X_FORWARDED_HOST',
    self::HEADER_CLIENT_PROTO => 'X_FORWARDED_PROTO',
    self::HEADER_CLIENT_PORT => 'X_FORWARDED_PORT',
);

/**
 * Returns the client IP addresses.
 *
 * In the returned array the most trusted IP address is first, and the
 * least trusted one last. The "real" client IP address is the last one,
 * but this is also the least trusted one. Trusted proxies are stripped.
 *
 * Use this method carefully; you should use getClientIp() instead.
 *
 * @return array The client IP addresses
 *
 * @see getClientIp()
 */
public function getClientIps()
{
    $clientIps = array();
    $ip = $this->server->get('REMOTE_ADDR');
    if (!$this->isFromTrustedProxy()) {
        return array($ip);
    }
    if (self::$trustedHeaders[self::HEADER_FORWARDED] && $this->headers->has(self::$trustedHeaders[self::HEADER_FORWARDED])) {
        $forwardedHeader = $this->headers->get(self::$trustedHeaders[self::HEADER_FORWARDED]);
        preg_match_all('{(for)=("?\[?)([a-z0-9\.:_\-/]*)}', $forwardedHeader, $matches);
        $clientIps = $matches[3];
    } elseif (self::$trustedHeaders[self::HEADER_CLIENT_IP] && $this->headers->has(self::$trustedHeaders[self::HEADER_CLIENT_IP])) {
        $clientIps = array_map('trim', explode(',', $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_IP])));
    }
    $clientIps[] = $ip; // Complete the IP chain with the IP the request actually came from
    $firstTrustedIp = null;
    foreach ($clientIps as $key => $clientIp) {
        // Remove port (unfortunately, it does happen)
        if (preg_match('{((?:\d+\.){3}\d+)\:\d+}', $clientIp, $match)) {
            $clientIps[$key] = $clientIp = $match[1];
        }
        if (!filter_var($clientIp, FILTER_VALIDATE_IP)) {
            unset($clientIps[$key]);
        }
        if (IpUtils::checkIp($clientIp, self::$trustedProxies)) {
            unset($clientIps[$key]);
            // Fallback to this when the client IP falls into the range of trusted proxies
            if (null ===  $firstTrustedIp) {
                $firstTrustedIp = $clientIp;
            }
        }
    }
    // Now the IP chain contains only untrusted proxies and the client IP
    return $clientIps ? array_reverse($clientIps) : array($firstTrustedIp);
}

Ответ 15

Я удивлен, что никто не упомянул filter_input, так что ответ Аликс Аксель сжат в одну строку:

function get_ip_address(&$keys = ['HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'HTTP_CLIENT_IP', 'REMOTE_ADDR'])
{
    return empty($keys) || ($ip = filter_input(INPUT_SERVER, array_pop($keys), FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE))? $ip : get_ip_address($keys);
}

Ответ 16

Ты очень много ответил на свой вопрос!:)

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;
}

Источник

Ответ 17

/**
 * Sanitizes IPv4 address according to Ilia Alshanetsky 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);
}