Qual é a maneira mais precisa de recuperar o endereço IP correto de um usuário em PHP?

301

Eu sei que há uma infinidade de cabeçalhos de variáveis $ _SERVER disponíveis para recuperação de endereço IP. Eu queria saber se existe um consenso geral sobre como recuperar com mais precisão o endereço IP real de um usuário (sabendo que nenhum método é perfeito) usando as referidas variáveis?

Passei algum tempo tentando encontrar uma solução detalhada e criei o seguinte código com base em várias fontes. Eu adoraria se alguém pudesse, por favor, abrir buracos na resposta ou lançar alguma luz sobre algo talvez mais preciso.

A edição inclui otimizações do @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;
 }

Palavras de Advertência (atualização)

REMOTE_ADDRainda representa a fonte mais confiável de um endereço IP. As outras $_SERVERvariáveis ​​mencionadas aqui podem ser falsificadas por um cliente remoto com muita facilidade. O objetivo desta solução é tentar determinar o endereço IP de um cliente sentado atrás de um proxy. Para seus propósitos gerais, considere usá-lo em combinação com o endereço IP retornado diretamente $_SERVER['REMOTE_ADDR']e armazenando os dois.

Para 99,9% dos usuários, esta solução atenderá perfeitamente às suas necessidades. Não o protegerá dos 0,1% de usuários mal-intencionados que desejam abusar do seu sistema injetando seus próprios cabeçalhos de solicitação. Se depender de endereços IP para algo de missão crítica, recorra REMOTE_ADDRe não se preocupe em atender àqueles atrás de um proxy.

Corey Ballou
fonte
2
Para a pergunta whatismyip.com, acho que eles fazem algo como esse script, você está executando localmente? Se você é por isso que possui um IP interno, nada está sendo enviado pela interface pública nesse caso, portanto, não há informações para o php obter
Matt
2
Certifique-se de manter isso em mente ao implementar esta: stackoverflow.com/questions/1672827/...
Kevin Peno
19
Lembre-se de que todos esses cabeçalhos HTTP são realmente fáceis de modificar: com a sua solução, só preciso configurar meu navegador para enviar um cabeçalho X-Forwarded-For com um IP aleatório e seu script retornará felizmente um endereço falso. Portanto, dependendo do que você está tentando fazer, esta solução pode ser menos confiável do que simplesmente usar REMOTE_ADDR.
gnomnain
14
OMFG, "ip não confiável"! Primeira vez que vejo uma bobagem aqui no SO. O único endereço IP confiável é REMOTE_ADDR
Seu senso comum
3
-1 isso é vulnerável à falsificação. Tudo o que você está fazendo é perguntar ao usuário qual deve ser o endereço IP dele.
rook

Respostas:

269

Aqui está uma maneira mais curta e limpa de obter o endereço 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;
                }
            }
        }
    }
}

Espero que ajude!


Seu código já parece bastante completo, não consigo ver nenhum erro possível (além das advertências de IP usuais), eu mudaria a validate_ip()função para depender da extensão do filtro:

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

Além disso, seu HTTP_X_FORWARDED_FORsnippet pode ser simplificado com isso:

// 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'];
    }
}

Para isso:

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

Você também pode validar endereços IPv6.

Alix Axel
fonte
4
Eu definitivamente aprecio a filter_varcorreção, pois remove um monte de verificações int não assinadas e hackeadas no endereço IP. Também gosto do fato de que também me dá a opção de validar endereços IPv6. A HTTP_X_FORWARDED_FORotimização também é muito apreciada. Em alguns minutos, atualizarei o código.
Corey Ballou #
33
-1 isso é vulnerável a falsificação, tudo o que você está fazendo é perguntar ao usuário qual deve ser o endereço IP dele.
rook
7
@Rook: Sim, eu sei. O OP está ciente disso, e eu também mencionei isso na minha resposta. Mas obrigado pelo comentário.
precisa
1
FYI: Eu tive que remover o FILTER_FLAG_IPV6 para que o código do Alix Axel funcionasse.
darkAsPitch
2
@ rubenrp81 O manipulador de soquete TCP é a única fonte canônica; todo o resto é controlado pelo invasor. O código acima é o sonho de um invasor.
torre
12

Mesmo assim, porém, obter o endereço IP real do usuário não será confiável. Tudo o que eles precisam fazer é usar um servidor proxy anônimo (um que não respeite os cabeçalhos de http_x_forwarded_for, http_forwarded, etc) e tudo o que você obtém é o endereço IP do servidor proxy.

Você pode ver se há uma lista de endereços IP do servidor proxy que são anônimos, mas não há como ter certeza de que é 100% preciso também, e o máximo que faria é informar que é um servidor proxy. E se alguém estiver sendo esperto, poderá falsificar cabeçalhos para encaminhamentos HTTP.

Digamos que eu não gosto da faculdade local. Eu descubro quais endereços IP eles registraram e obtenho seu endereço IP banido no seu site fazendo coisas ruins, porque eu acho que você respeita o encaminhamento HTTP. A lista não tem fim.

Depois, como você adivinhou, endereços IP internos, como a rede da faculdade que eu criei antes. Muito use um formato 10.xxx. Então, tudo que você saberia é que foi encaminhado para uma rede compartilhada.

Então não vou começar muito, mas os endereços IP dinâmicos são o caminho da banda larga. Assim. Mesmo que você obtenha um endereço IP de usuário, espere que ele mude em 2 a 3 meses, no máximo.

Peter Mortensen
fonte
Obrigado pela contribuição. Atualmente, estou utilizando o endereço IP do usuário para ajudar na autenticação da sessão usando o IP da classe C como um fator limitante para limitar o seqüestro de sessão, mas permitindo IPs dinâmicos dentro do motivo. IPs falsificados e servidores proxy anônimos são apenas algo que eu tenho que lidar com um grupo seleto de indivíduos.
Corey Ballou
@cballou - Certamente para esse propósito, REMOTE_ADDR é o correto a ser usado. Qualquer abordagem baseada em cabeçalhos HTTP é vulnerável à falsificação de cabeçalho. Quanto tempo dura uma sessão? IPs dinâmicos não mudam rapidamente.
MZB
Eles fazem, especialmente se eu quiser (mude o endereço mac que muitos drivers suportam). Apenas o REMOTE_ADDR, por si só, é suficiente para obter qual foi o último servidor com o qual ele conversou. Portanto, em uma situação de proxy, você obtém o IP do proxy.
8

Nós usamos:

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

A explosão no HTTP_X_FORWARDED_FOR é devido a problemas estranhos que detectamos endereços IP quando o Squid foi usado.

gabrielk
fonte
Opa, eu acabei de perceber que você faz basicamente a mesma coisa com a explosão, e assim por diante. Além de um pouco mais. Portanto, duvido que minha resposta tenha sido de grande ajuda. :)
gabrielk
Este endereço de retorno do localhost
Scarl
3

Minha resposta é basicamente apenas uma versão polida, totalmente validada e totalmente empacotada da resposta do @ 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 = '';
    };

?>

Alterar:

  • Simplifica o nome da função (com o estilo de formatação 'camelCase').

  • Ele inclui uma verificação para garantir que a função ainda não esteja declarada em outra parte do seu código.

  • Ele leva em consideração a compatibilidade do 'CloudFlare'.

  • Inicializa vários nomes de variáveis ​​"relacionadas ao IP" no valor retornado da função 'getClientIP'.

  • Ele garante que, se a função não retornar um endereço IP válido, todas as variáveis ​​serão definidas como uma sequência vazia, em vez de null.

  • São apenas (45) linhas de código.

James Anderson Jr.
fonte
2

A maior questão é para que finalidade?

Seu código é quase tão abrangente quanto poderia ser - mas vejo que, se você encontrar o que parece um cabeçalho de proxy adicionado, você usa esse INSTEAD do CLIENT_IP; no entanto, se você deseja que essas informações para fins de auditoria sejam avisadas - é muito fácil fingir.

Certamente você nunca deve usar endereços IP para qualquer tipo de autenticação - mesmo estes podem ser falsificados.

Você pode obter uma melhor medição do endereço IP do cliente enviando um applet flash ou java que se conecta ao servidor por uma porta não http (o que revelaria proxies transparentes ou casos em que os cabeçalhos injetados por proxy são falsos - mas lembre-se de que, onde o cliente SÓ pode se conectar por meio de um proxy da Web ou a porta de saída estiver bloqueada, não haverá conexão com o applet.

C.

symcbean
fonte
Considerando que estou procurando uma solução apenas para PHP, você está sugerindo que eu adicione $_SERVER['CLIENT_IP']como a segunda declaração if?
Corey Ballou
Não - apenas se você quiser colocar algum significado nos dados retornados, seria uma boa ideia preservar o endereço do ponto final da rede (IP do cliente), além de qualquer coisa que sugira um valor diferente nos cabeçalhos de proxy adicionados (por exemplo, você pode ver muitas 192.168.1.X endereços mas vindo de cliente diferente iPS) C.
symcbean
1

Sei que há respostas muito melhores e mais concisas acima, e isso não é uma função nem o script mais elegante possível. No nosso caso, precisávamos emitir o x_forwarded_for spoofable e o remote_addr mais confiável em um switch simplista, por assim dizer. Ele precisava permitir que espaços em branco injetassem em outras funções se-nenhum ou se-singular (em vez de apenas retornar a função pré-formatada). Ele precisava de um var "ativado ou desativado" com etiquetas personalizadas por comutador para as configurações da plataforma. Ele também precisava de uma maneira para o $ ip ser dinâmico, dependendo da solicitação, para que ele assumisse a forma de forwarded_for.

Também não vi ninguém endereço isset () vs! Empty () - é possível inserir nada para x_forwarded_for ainda assim acionar a verdade isset () que resulta em var em branco, uma maneira de contornar é usar && e combinar ambos como condições. Lembre-se de que você pode falsificar palavras como "PWNED" como x_forwarded_for, portanto, certifique-se de esterilizar para uma sintaxe de IP real se estiver produzindo algum lugar protegido ou no DB.

Além disso, você pode testar usando o google translate se precisar de um proxy múltiplo para ver a matriz em x_forwarder_for. Se você deseja testar os cabeçalhos de falsificação, confira esta extensão do Spoof de cabeçalho do cliente Chrome . O padrão será apenas o remote_addr padrão enquanto estiver atrás do proxy anon.

Eu não sei em nenhum caso em que remote_addr possa estar vazio, mas está lá como substituto por precaução.

// 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 = '';
}

Para torná-los dinâmicos para uso em funções ou consulta / eco / visualizações abaixo, digamos para geração de log ou relatório de erros, use globais ou apenas faça eco onde quiser, sem criar muitas outras condições ou saída de esquema estático funções.

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

Obrigado por todos os seus grandes pensamentos. Por favor, deixe-me saber se isso poderia ser melhor, ainda meio novo para esses cabeçalhos :)

dhaupin
fonte
1

Eu vim com essa função que não simplesmente retorna o endereço IP, mas uma matriz com informações de 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;
}

Aqui está a função:

/**
 * 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;
}
Philipp
fonte
1

Como alguém disse anteriormente, a chave aqui é por que motivo você deseja armazenar os ips do usuário.

Vou dar um exemplo de um sistema de registro em que trabalho e, é claro, a solução apenas para contribuir com essa discussão antiga que aparece frequentemente em minhas pesquisas.

Muitas bibliotecas de registro php usam ip para limitar / bloquear tentativas com falha com base no ip do usuário. Considere esta tabela:

-- 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
...

Em seguida, quando um usuário tenta fazer um login ou qualquer coisa relacionada ao serviço, como uma redefinição de senha, uma função é chamada no início:

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

Digamos, por exemplo, $this->token->get('attempts_before_ban') === 10e 2 usuários venham para os mesmos ips, como é o caso nos códigos anteriores, onde os cabeçalhos podem ser falsificados ; depois de 5 tentativas, cada um é banido ! Pior ainda, se todos vierem do mesmo proxy, somente os 10 primeiros usuários serão registrados e todo o resto será banido!

O essencial aqui é que precisamos de um índice exclusivo na tabela attemptse podemos obtê-lo de uma combinação como:

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

onde jwt_loadvem de um cookie HTTP que segue o json símbolo web tecnologia onde armazenamos somente o criptografado carga que deve conter um valor arbitrário / exclusivo para cada usuário. Obviamente, a solicitação deve ser modificada para: "SELECT count(*) FROM {$this->token->get('table_attempts')} WHERE ip = ? AND jwt_load = ?"e a classe também deve iniciar a private $jwt.

centuriano
fonte
0

Eu me pergunto se talvez você deva repetir o HTTP_X_FORWARDED_FOR explodido em ordem inversa, já que minha experiência tem sido que o endereço IP do usuário termina no final da lista separada por vírgulas; portanto, começando no início do cabeçalho, você está maior probabilidade de obter o endereço IP de um dos proxies retornados, o que potencialmente ainda pode permitir o seqüestro de sessões, pois muitos usuários podem passar por esse proxy.

Chris Withers
fonte
1
Depois de ler a página da wikipedia em HTTP_X_FORWARDED_FOR: en.wikipedia.org/wiki/X-Forwarded-For ... Vejo que a ordem sugerida é, de fato, da esquerda para a direita, conforme seu código. No entanto, em nossos logs, vejo que há muitos casos em que isso não é respeitado por proxies em estado selvagem e o endereço IP que você deseja verificar pode estar nos dois extremos da lista.
Chris Withers
1
Ou no meio, como aconteceria se alguns dos procuradores respeitassem a ordem da esquerda para a direita e outros não.
Brilliand
0

Obrigado por isso, muito útil.

Seria útil se o código estivesse sintaticamente correto. No momento, existem muitos em torno da linha 20. O que eu receio significa que ninguém realmente tentou isso.

Eu posso estar louco, mas depois de tentar em alguns endereços válidos e inválidos, a única versão do validate_ip () que funcionou foi a seguinte:

    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;
    }
Mark Boon
fonte
0

Aqui está uma versão modificada se você usar os serviços da camada de cache 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;
}
jmserra
fonte
0

Apenas uma versão do VB.NET da resposta:

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
Ábaco
fonte
3
Existe a tag "PHP" na pergunta
luchaninov 16/02
0

Apenas outra maneira limpa:

  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;
  }
Liko
fonte
0

Da classe Request do 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);
}
luchaninov
fonte
Propriedade indefinida: $ server
C47
0

Estou surpreso que ninguém tenha mencionado filter_input, então aqui está a resposta de Alix Axel resumida em uma linha:

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);
}
mattavatar
fonte
-1

Você praticamente respondeu sua própria pergunta! :)

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

Fonte

Alex Weber
fonte
-6
/**
 * 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);
}
Meketrefe
fonte