Obter o endereço IP real do cliente no Heroku

Na Heroku Cedar, eu queria obter o IP do cliente. Primeira tentativa:

ENV['REMOTE_ADDR'] 

Isso não funciona, é claro, porque todas as solicitações são passadas por proxies. Então a alternativa era usar:

 ENV['HTTP_X_FORWARDED_FOR'] 

Mas isso não é bem seguro, é?

Se ele contém apenas um valor, eu levo isso. Se ele contiver mais de um valor (separado por vírgula), eu poderia pegar o primeiro.

Mas e se alguém manipular esse valor? Eu não posso confiar em ENV['HTTP_X_FORWARDED_FOR'] como eu poderia com ENV['REMOTE_ADDR'] . E também não há lista de proxies confiáveis ​​que eu possa usar.

Mas deve haver alguma maneira de obter sempre o endereço IP do cliente. Você conhece um?

Em seus documentos , Heroku descreve que X-Forwarded-For é “o endereço IP de origem do cliente que se conecta ao roteador Heroku”.

Isso soa como se Heroku pudesse sobrescrever o X-Forwarded-For com o IP remoto de origem. Isso evitaria spoofing, certo? Alguém pode verificar isso?

De Jacob, o diretor de segurança da Heroku na época:

O roteador não sobrescreve o X-Forwarded-For , mas garante que a origem real sempre será o último item da lista.

Isto significa que, se você acessar um aplicativo Heroku da maneira normal, você verá apenas o seu endereço IP no header X-Forwarded-For :

 $ curl http://httpbin.org/ip { "origin": "123.124.125.126", } 

Se você tentar falsificar o IP, sua origem alegada é refletida, mas – criticamente – também o seu IP real. Obviamente, isso é tudo o que precisamos, portanto, há uma solução clara e segura para obter o endereço IP do cliente no Heroku:

 $ curl -H"X-Forwarded-For: 8.8.8.8" http://httpbin.org/ip { "origin": "8.8.8.8, 123.124.125.126" } 

Isto é exatamente o oposto do que é descrito na Wikipedia , a propósito.

Implementação PHP:

 function getIpAddress() { if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { $ipAddresses = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']); return trim(end($ipAddresses)); } else { return $_SERVER['REMOTE_ADDR']; } } 

Eu trabalho no departamento de suporte da Heroku e passei algum tempo discutindo isso com nossos engenheiros de roteamento. Eu queria postar algumas informações adicionais para esclarecer algumas coisas sobre o que está acontecendo aqui.

O exemplo fornecido na resposta acima apenas mostrava o IP do cliente por coincidência e isso não é realmente garantido. O motivo pelo qual não foi o primeiro é porque a solicitação de origem afirmou que estava encaminhando para o IP especificado no header X-Forwarded-For . Quando o roteador Heroku recebeu a solicitação, ele apenas anexou o IP que estava se conectando diretamente à lista X-Forwarded-For após o que havia sido injetado na solicitação. Nosso roteador sempre adiciona o IP que está conectado ao AWS ELB na frente da nossa plataforma como o último IP da lista. Esse IP pode ser o original (e, no caso de haver apenas um IP, é quase certo), mas no instante em que há vários IPs encadeados, todas as apostas estão desativadas. A convenção é sempre adicionar o último IP da cadeia ao final da lista (que é o que fazemos), mas em qualquer ponto ao longo da cadeia essa cadeia pode ser alterada e diferentes IPs podem ser inseridos. Assim, o único IP confiável (da perspectiva da nossa plataforma) é o último IP da lista.

Para ilustrar, digamos que alguém inicie uma solicitação e adicione arbitrariamente 3 IPs adicionais ao header X-Forwarded-For:

 curl -H "X-Forwarded-For: 12.12.12.12,15.15.15.15,4.4.4.4" http://www.google.com 

Imagine que o IP desta máquina era 9.9.9.9 e que ela tinha que passar por um proxy (por exemplo, um proxy para toda a universidade). Digamos que o proxy tenha um IP de 2.2.2.2. Assumindo que não foi configurado para remover headers X-Forwarded-For (o que provavelmente não seria), seria apenas append o IP 9.9.9.9 ao final da lista e passar o pedido para o Google. Neste ponto, o header ficaria assim:

 X-Forwarded-For: 12.12.12.12,15.15.15.15,4.4.4.4,9.9.9.9 

Essa solicitação passará pelo ponto de extremidade do Google, que appendá o IP do proxy da universidade de 2.2.2.2, para que o header fique assim nos registros do Google:

 X-Forwarded-For: 12.12.12.12,15.15.15.15,4.4.4.4,9.9.9.9,2.2.2.2 

Então, qual é o IP do cliente? É impossível dizer do ponto de vista do Google. Na realidade, o IP do cliente é 9.9.9.9. O último IP listado é o 2.2.2.2 e o primeiro é 12.12.12.12. Tudo o que o Google sabe é que o IP 2.2.2.2 está definitivamente correto, porque esse era o IP que realmente estava conectado ao seu serviço – mas eles não saberiam se esse era o cliente inicial da solicitação ou não dos dados disponíveis. Da mesma forma, quando há apenas um IP nesse header – ou seja, o IP que está diretamente conectado ao nosso serviço, sabemos que é confiável.

Do ponto de vista prático, esse IP provavelmente será confiável na maior parte do tempo (porque a maioria das pessoas não se importará em falsificar seu IP). Infelizmente, é impossível evitar esse tipo de spoofing e, quando uma solicitação chega ao roteador Heroku, é impossível saber se os IPs em uma cadeia X-Forwarded-For foram adulterados ou não.

Deixando de lado todas as questões de confiabilidade, essas cadeias de IP sempre devem ser lidas da esquerda para a direita. O IP do cliente deve ser sempre o IP mais à esquerda.

Você nunca pode realmente confiar em qualquer informação vinda do cliente. É mais uma questão de quem você confia e como você o verifica. Mesmo Heroku pode ser influenciado para fornecer um valor HTTP_X_FORWARDED_FOR ruim se eles tiverem um bug no código, ou se forem hackeados de alguma forma. Outra opção seria alguma outra máquina Heroku conectando-se ao seu servidor internamente e ignorando o seu proxy ao mesmo tempo que falsificando REMOTE_ADDR e / ou HTTP_X_FORWARDED_FOR .

A melhor resposta aqui dependeria do que você está tentando fazer. Se você está tentando verificar seus clientes, um certificado do lado do cliente pode ser uma solução mais apropriada. Se tudo o que você precisa do IP for localização geográfica, confiar na input pode ser bom o suficiente. Na pior das hipóteses, alguém falsificará o local e obterá o conteúdo errado … Se você tiver um caso de uso diferente, há muitas outras soluções entre esses dois extremos.