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_ADDR
ainda representa a fonte mais confiável de um endereço IP. As outras $_SERVER
variá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_ADDR
e não se preocupe em atender àqueles atrás de um proxy.
fonte
Respostas:
Aqui está uma maneira mais curta e limpa de obter o endereço 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:Além disso, seu
HTTP_X_FORWARDED_FOR
snippet pode ser simplificado com isso:Para isso:
Você também pode validar endereços IPv6.
fonte
filter_var
correçã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. AHTTP_X_FORWARDED_FOR
otimização também é muito apreciada. Em alguns minutos, atualizarei o código.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.
fonte
Nós usamos:
A explosão no HTTP_X_FORWARDED_FOR é devido a problemas estranhos que detectamos endereços IP quando o Squid foi usado.
fonte
Minha resposta é basicamente apenas uma versão polida, totalmente validada e totalmente empacotada da resposta do @ AlixAxel:
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.
fonte
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.
fonte
$_SERVER['CLIENT_IP']
como a segunda declaração if?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.
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.
Obrigado por todos os seus grandes pensamentos. Por favor, deixe-me saber se isso poderia ser melhor, ainda meio novo para esses cabeçalhos :)
fonte
Eu vim com essa função que não simplesmente retorna o endereço IP, mas uma matriz com informações de IP.
Aqui está a função:
fonte
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:
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:
Digamos, por exemplo,
$this->token->get('attempts_before_ban') === 10
e 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
attempts
e podemos obtê-lo de uma combinação como:onde
jwt_load
vem 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 aprivate $jwt
.fonte
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.
fonte
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:
fonte
Aqui está uma versão modificada se você usar os serviços da camada de cache CloudFlare
fonte
Apenas uma versão do VB.NET da resposta:
fonte
Apenas outra maneira limpa:
fonte
Da classe Request do Symfony https://github.com/symfony/symfony/blob/1bd125ec4a01220878b3dbc3ec3156b073996af9/src/Symfony/Component/HttpFoundation/Request.php
fonte
Estou surpreso que ninguém tenha mencionado filter_input, então aqui está a resposta de Alix Axel resumida em uma linha:
fonte
Você praticamente respondeu sua própria pergunta! :)
Fonte
fonte
fonte