Nginx responde com 200, mesmo quando o cabeçalho HTTP HOST é diferente do SNI

1

Acabei de começar a aprender sobre o nginx e o SSL e não me importo com configurações diferentes. Eu tenho dois blocos de servidores como o seguinte:

server{
    listen 443 ssl http2;
    server_name www.a.com;
    ssl_certificate a.crt;
    ssl_certificate_key a.key;
    ....
}
server{
    listen 443 ssl ;
    server_name www.b.com;
    ssl_certificate b.crt;
    ssl_certificate_key b.key;
    ....
}

Após conectar-se ao nginx através do openssl s_client usando www.a.com como meu SNI, enviei uma solicitação GET usando o Host: www.b.com e ainda funciona. Isso é esperado? Alguém pode me ajudar a entender o comportamento do nginx?

    
por sank 17.04.2018 / 01:42

1 resposta

3

Uma conexão HTTP é apenas uma conexão TCP no final do dia. Então, o que está acima é realmente bom para conexões HTTP (ou seja, se você se conectar a www.a.com, você está realmente se conectando a um endereço IP e não a www.a.com, então é realmente bom e esperado que essa conexão seja capaz de endereço de pedidos para www.b.com).

Para HTTPS, como você está dizendo, é um pouco menos claro ...

Para HTTP / 1.1, não há nada em HTTP sobre TLS RFC nem o Hypertext Transfer Protocol (HTTP / 1.1): Sintaxe de mensagem e RFC de roteamento , para indicar como isso deve ser tratado, pois o SNI é uma extensão não obrigatória para o HTTP adicionado depois (na verdade, como um aparte essa era uma maneira de contornar clientes - como o IE8 no XP - se o mesmo certificado fosse usado para ambos os hosts virtuais, a conexão apropriada poderia ser usada após a sessão TLS ser negociada com base nesse certificado conforme fornecido pelo host padrão). No entanto, o SNI RFC afirma claramente que o servidor DEVE verificar se este é o mesmo , portanto, quando um nome de servidor for dado e é diferente para o host, o que você está observando está incorreto (graças a Michael Hampton por apontar isso como eu perdi isso originalmente).

O HTTP / 2 é mais claro e, de fato, permite a reutilização da conexão em determinadas condições e tem isto a dizer :

A connection can be reused as long as the origin server is authoritative (Section 10.1). For TCP connections without TLS, this depends on the host having resolved to the same IP address.

For https resources, connection reuse additionally depends on having a certificate that is valid for the host in the URI. The certificate presented by the server MUST satisfy any checks that the client would perform when forming a new TLS connection for the host in the URI.

An origin server might offer a certificate with multiple subjectAltName attributes or names with wildcards, one of which is valid for the authority in the URI. For example, a certificate with a subjectAltName of *.example.com might permit the use of the same connection for requests to URIs starting with https://a.example.com/ and https://b.example.com/.

Portanto, se a.crt também inclui include www.b.com no campo Subject Alternative Name, então acima está realmente bem sob HTTP / 2, embora não sob HTTP / 1.1.

No entanto, só para ter certeza, eu repeti suas observações quando o a.crt definitivamente NÃO cobre o www.b.com e isso realmente soa como um bug e também potencialmente um risco de segurança, como um pedido para um virtual host poderia ser lido por outro host virtual (apesar de não ter acesso à chave com a qual a mensagem foi descriptografada) se alguém conseguir injetar um cabeçalho de host inválido (o que, felizmente, os navegadores não facilitam - portanto, não posso demonstrar isso) . Isso pode vazar cookies ou outras informações para o servidor secundário.

Eu a levantei para a equipe de segurança do nginx que diz que não é uma falha de segurança com o nginx e que cabe ao cliente validar certificados e não enviar solicitações malformadas de hosts que eles devem acessar sobre essa conexão. Eu discordo totalmente disso, e digo que o ponto inteiro do HTTPS é permitir apenas que os endpoints válidos não criptografem as mensagens! Portanto, acho que a verificação deve estar no cliente e no lado do servidor e, de fato, esse cenário exato foi discutido pelo grupo de trabalho TLS na época que o SNI era específico, e é por isso que o texto acima foi adicionado ao RFC . Além disso, a equipe de segurança da nginx diz que neste cenário nginx deve ser configurado com um certificado conjunto para ambos os hosts (o que não faz sentido para mim, pois limita muito a utilidade da hospedagem virtual no nginx!), Mas se um operador nginx realmente quer restringir isso, então eles podem fazer verificando a variável $ssl_server_name (o que para mim significa que é uma verificação relativamente fácil que o nginx poderia fazer por padrão!). De qualquer forma, é o software deles e, portanto, eles têm direito a sua opinião, mas eu discordo.

Também foi apontado que essa técnica às vezes é usada para evitar a censura em um processo conhecido como Domain Fronting . O que é justo o suficiente, mas que eu acho que deve ser explicitamente habilitado em vez de ser permitido por padrão.

Incidentalmente o Apache não faz o mesmo e (corretamente IMHO) retorna um "400 Bad Request" antes de 2.4.17 e o novo "421 (Misdirected Request)" depois de 2.4.17 - que era um novo código de status HTTP criado especialmente para esse cenário .

    
por 17.04.2018 / 19:56