nginx real_ip_header e X-Forwarded-For parece errado

59

A descrição da Wikipedia do cabeçalho HTTP X-Forwarded-Foré:

Encaminhado para X: cliente1, proxy1, proxy2, ...

A documentação nginx para a diretiva real_ip_headerdiz, em parte:

Esta diretiva define o nome do cabeçalho usado para transferir o endereço IP de substituição.
No caso de X-Forwarded-For, este módulo usa o último ip no cabeçalho X-Forwarded-For para substituição. [Ênfase minha]

Essas duas descrições parecem estar em desacordo. Em nosso cenário, o X-Forwarded-Forcabeçalho é exatamente como descrito - o endereço IP "real" do cliente é a entrada mais à esquerda. Da mesma forma, o comportamento do nginx é usar o valor mais à direita - que, obviamente, é apenas um de nossos servidores proxy.

Meu entendimento X-Real-IPé que ele deve ser usado para determinar o endereço IP real do cliente - não o proxy. Estou faltando alguma coisa, ou isso é um bug no nginx?

E, além disso, alguém tem alguma sugestão de como fazer com que o X-Real-IPcabeçalho exiba o valor mais à esquerda , conforme indicado pela definição de X-Forwarded-For?

Kirk Woll
fonte

Respostas:

97

Acredito que a chave para resolver problemas com o X-Forwarded-For quando vários IPs estão encadeados é a opção de configuração recentemente introduzida real_ip_recursive(adicionada no nginx 1.2.1 e 1.3.0). Nos documentos do nginx realip :

Se a pesquisa recursiva estiver ativada, um endereço de cliente original que corresponda a um dos endereços confiáveis ​​será substituído pelo último endereço não confiável enviado no campo de cabeçalho da solicitação.

O nginx estava pegando o último endereço IP da cadeia por padrão, porque esse era o único que era considerado confiável. Mas com o novo real_ip_recursiveativado e com várias set_real_ip_fromopções, você pode definir vários proxies confiáveis ​​e ele buscará o último IP não confiável.

Por exemplo, com esta configuração:

set_real_ip_from 127.0.0.1;
set_real_ip_from 192.168.2.1;
real_ip_header X-Forwarded-For;
real_ip_recursive on;

E um cabeçalho X-Forwarded-For resultando em:

X-Forwarded-For: 123.123.123.123, 192.168.2.1, 127.0.0.1

O nginx agora selecionará 123.123.123.123 como o endereço IP do cliente.

Quanto ao motivo pelo qual o nginx não escolhe apenas o endereço IP mais à esquerda e exige que você defina explicitamente proxies confiáveis, é para impedir a falsificação fácil de IP.

Digamos que o endereço IP real de um cliente seja 123.123.123.123. Digamos também que o cliente não é bom, e eles estão tentando falsificar seu endereço IP 11.11.11.11. Eles enviam uma solicitação ao servidor com este cabeçalho já instalado:

X-Forwarded-For: 11.11.11.11

Como os proxies reversos simplesmente adicionam IPs a essa cadeia X-Forwarded-For, digamos que ele se parece com isso quando o nginx chega a ele:

X-Forwarded-For: 11.11.11.11, 123.123.123.123, 192.168.2.1, 127.0.0.1

Se você simplesmente pegasse o endereço mais à esquerda, isso permitiria que o cliente falsificasse facilmente seu endereço IP. Mas com o exemplo acima da configuração nginx, o nginx confiará apenas nos dois últimos endereços como proxies. Isso significa que o nginx escolherá corretamente 123.123.123.123o endereço IP, apesar de o IP falsificado ser o mais à esquerda.

Nick M
fonte
2
Muito obrigado por isso, isso realmente me ajudou. Essa deve ser a resposta aceita.
José F. Romaniello 26/12/2013
11
Por padrão, real_ip_header parece ser X-Real-IP de acordo com nginx.org/en/docs/http/ngx_http_realip_module.html Isso significa que um usuário mal-intencionado pode simplesmente enviar uma solicitação com X-Real-IP aleatório e que será usado como $ remote_addr no nginx (e também possivelmente passado para o aplicativo)?
gansbrest
@gansbrest Não, porque set_real_ip_from limita os hosts confiáveis.
El Yobo
9

A análise do X-Forwarded-Forcabeçalho é realmente falha no módulo nginx real_ip.

len = r->headers_in.x_forwarded_for->value.len;
ip = r->headers_in.x_forwarded_for->value.data;

for (p = ip + len - 1; p > ip; p--) {
  if (*p == ' ' || *p == ',') {
    p++;
    len -= p - ip;
    ip = p;
    break;
  }
}

Inicia na extremidade direita da cadeia de cabeçalho e, assim que vê um espaço ou vírgula, para de procurar e cola a parte à direita do espaço ou vírgula na variável IP. Portanto, trata o endereço de proxy mais recente como o endereço de cliente original .

Não está se saindo bem de acordo com as especificações; esse é o risco de não ser explicitado em termos dolorosamente óbvios em uma RFC.

Além disso: é difícil até encontrar uma boa fonte primária no formato, que foi originalmente definido pelo Squid - uma escavação na documentação confirma a ordem; mais à esquerda é o cliente original, mais à direita é o anexo mais recente. Estou extremamente tentado a adicionar uma [citação necessária] a essa página da Wikipedia. Uma edição anônima parece ser a autoridade da Internet sobre o assunto.

Se possível, você pode fazer com que seus proxies intermediários parem de se adicionar ao final do cabeçalho, deixando-o apenas com o endereço do cliente real?

Shane Madden
fonte
Obrigado pela resposta, @ Shane. De fato, ao atingir o nginx, um X-Forwarded-Forjá existe. (é o endereço IP do cliente correto), o próprio nginx passa a acrescentar o endereço IP do nosso balanceador de carga (o salto anterior) ao X-Forwarded-Forcabeçalho. (presumivelmente anexando o que ele vê como "endereço remoto"). Se simplesmente não fizesse isso, seria capaz de usar o X-Forwarded-Forcabeçalho como antes. (temos sido recentemente migrando para nginx)
Kirk Woll
@ Kirk Então, quando o nginx obtém o cabeçalho, é apenas o endereço do cliente original? Mas quando está processando, é adicionado no cabeçalho do servidor proxy de conexão? Isso não contribui - o único momento em que ele deve tocar nesse cabeçalho é quando está enviando a conexão para outro proxy por meio de um proxy_pass- e, mesmo assim, apenas proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;no local.
Shane Madden
Até o W3C erra : a documentação indica "proxies devem adicionar o endereço IP do iniciador da solicitação ao final de uma lista separada por vírgulas em um campo de cabeçalho X-Forwarded-For HTTP", deve indicar o início .
Ian Kemp
3
@IanKemp, não, o fim está correto. Para o lado do servidor de um proxy, o iniciador da solicitação (ou seja, solicitação TCP ) é o proxy anterior (se houver). Esse proxy anterior possivelmente já envia um X-Forwarded-Forcabeçalho com o endereço do cliente original à esquerda e possivelmente os proxies anexados a ele. Portanto, o proxy atualmente em exibição adiciona o proxy anterior (= iniciador) ao final dessa lista e serve o X-Forwarded-Forcabeçalho assim aumentado para o próximo salto upstream. É verdade que eles poderiam ter escolhido uma redação mais óbvia.
precisa saber é o seguinte
5

X-Real-IP é o endereço IP do cliente real com o qual o servidor está falando (o cliente "real" do servidor), que, no caso de uma conexão com proxy, é o servidor proxy. É por isso que o X-Real-IP conterá o último IP no cabeçalho X-Forwarded-For.

user558061
fonte
11
OK, mas, para mim, isso simplesmente nunca é uma informação útil. Quero obter o endereço IP original do cliente - isso é crucial e, de acordo com tudo que li, o objetivo desses cabeçalhos. Por que eu gostaria de saber o endereço IP dos nossos servidores proxy?
22411 Kirk Woll
Se não é útil para você, então não é para você. Ninguém está forçando você a usar o X-Real-IP. Se você precisar do IP do usuário em seu aplicativo, faça com que ele analise o X-Forwarded-For (o que nem sempre é confiável, porque existem alguns proxies (dispositivo de segurança da Internet / firewalls) que não definem o X-Forwarded- Para). No contexto do nginx, o X-Forwarded-For não é importante porque não está falando com esses clientes, além da última entrada (o X-Real-IP), que é o cliente do nginx. Se você não precisar, não o defina, desative-o ou ignore-o: /
user558061
2
Não, o que eu quero dizer é, por que X-Real-IPo retorno endereço IP do meu próprio servidor proxy sempre ser útil?
22411 Kirk Woll
Ótimo .. responda cara. Eu estava procurando por esta informação precisa. Preciso conversar com um servidor ncat no meu servidor proxy, por isso preciso disso rapidamente.
Yugal Jindle