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

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

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

edit включает оптимизацию из @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 и не утруждайте себя обслуживанием тех, кто за a полномочие.

16 ответов


вот более короткий, более чистый способ получить 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-оговорок), я бы изменил


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

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

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

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

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


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

/**
 * Get the customer's 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-адресов, когда кальмаров.


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

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

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

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

С.


просто 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

Я понимаю, что есть гораздо лучшие и более краткие ответы выше, и это не функция и не самый изящный сценарий. В нашем случае нам нужно было вывести как поддельный x_forwarded_for, так и более надежный remote_addr в упрощенном коммутаторе per-say. Он должен был разрешить пробелы для инъекции в другие функции, если-нет или если-сингулярно (а не просто возвращать предварительно отформатированную функцию). Для этого нужна была "on или off" ВАР с каждого выключателя этикетки(ы) для настройки платформы. Ему также нужен был способ для $ip быть динамическим в зависимости от запроса, чтобы он принял форму forwarded_for.

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

кроме того, вы можете проверить с помощью Google translate, если вам нужен мульти-прокси, чтобы увидеть массив в x_forwarder_for. Если вы хотите подделать заголовки для тестирования, проверьте это Chrome Client Header Spoof


Я придумал эту функцию, которая не просто возвращает 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'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.
 *
 * @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's a proxy!
    if ( $forwarded != $result->ip ) {
        $result->proxy = true;
        $result->proxy_ip = $result->ip;
        $result->ip = $forwarded;
    }

    return $result;
}

мой ответ в основном просто полированная, полностью проверенная и полностью упакованная версия ответа @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) строк кода.


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


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

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

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

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

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

из класса запросов Symfony 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);
}

как кто-то сказал ранее, ключ здесь по какой причине вы хотите сохранить 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
...

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

public function isBlocked() {
      /*
       * used one of the above methods to capture user's 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 в веб-маркер технология, где мы храним только зашифрованные полезной нагрузки, что должны содержать произвольное/уникальное значение для каждого пользователя. Конечно, запрос должен быть изменен на: "SELECT count(*) FROM {$this->token->get('table_attempts')} WHERE ip = ? AND jwt_load = ?" и класс также должен инициировать private $jwt.


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

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

источник


/**
 * 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);
}