Introdução à rede QUIC e à criptografia no NGINX

NGINX | 19 de abril de 2023

A primeira menção ao QUIC e ao HTTP/3 no blog da NGINX foi há quatro anos (!), e, assim como você, agora aguardamos ansiosamente a iminente incorporação da nossa implementação do QUIC à ramificação principal do NGINX Open Source. Dado o longo período de desenvolvimento, é compreensível que você não tenha pensado muito no QUIC.

Neste momento, porém, como desenvolvedor ou administrador de sites, você precisa estar ciente de como o QUIC transfere a responsabilidade por alguns detalhes de rede do sistema operacional para o NGINX (e todos os aplicativos HTTP). Mesmo que redes não sejam a sua praia, adotar o QUIC significa que se preocupar com a rede agora faz parte (pelo menos um pouco) do seu trabalho.

Neste post, exploramos os principais conceitos de rede e criptografia usados no QUIC, simplificando alguns detalhes e omitindo informações não essenciais para tornar a explicação mais clara. Embora algumas nuances possam se perder nesse processo, nossa intenção é fornecer informações suficientes para que você possa adotar o QUIC de forma eficaz em seu ambiente ou, pelo menos, estabelecer uma base para ampliar seus conhecimentos.

Se o QUIC for uma novidade para você, recomendamos que primeiro leia uma de nossas publicações anteriores e assista ao nosso vídeo introdutório.

 

Para uma explicação mais detalhada e completa sobre o QUIC, recomendamos o excelente documento Gerenciabilidade do Protocolo de Transporte QUIC do grupo de trabalho QUIC da IETC, além dos materiais complementares indicados ao longo deste documento.

Por que você deveria se importar com redes e criptografia no QUIC?

Até agora, os detalhes mais complexos da conexão de rede entre os clientes e o NGINX não eram particularmente relevantes para a maioria dos usuários. Afinal, com HTTP/1.x e HTTP/2, o sistema operacional se encarrega de estabelecer a conexão do Protocolo de Controle de Transmissão (TCP) entre os clientes e o NGINX. O NGINX simplesmente usa a conexão depois que ela é estabelecida.

Com o QUIC, no entanto, a responsabilidade pela criação, validação e gestão das conexões passa do sistema operacional subjacente para o NGINX. Em vez de receber uma conexão TCP já estabelecida, o NGINX agora recebe um fluxo de datagramas do Protocolo de Datagrama do Usuário (UDP), que precisa interpretar como conexões e fluxos de clientes. O NGINX também passa a ser responsável por lidar com a perda de pacotes, o restabelecimento de conexões e o controle de congestionamento.

Além disso, o QUIC combina o início da conexão, a negociação de versão e a troca de chaves de criptografia em uma única operação de estabelecimento de conexão. E, embora a criptografia TLS seja tratada de maneira bastante semelhante tanto no QUIC+HTTP/3 quanto no TCP+HTTP/1+2, há diferenças que podem ser significativas para dispositivos subsequentes, como balanceadores de carga de camada 4, firewalls e appliances de segurança.

Em última análise, o efeito geral dessas mudanças é uma experiência mais segura, rápida e confiável para os usuários, com pouquíssimas alterações na configuração ou nas operações do NGINX. No entanto, os administradores do NGINX precisam entender pelo menos um pouco do que está acontecendo com o QUIC e o NGINX, nem que seja apenas para manter o tempo médio até a comprovação de inocência o mais curto possível em caso de problemas.

(Vale observar que, embora este artigo se concentre nas operações HTTP porque o HTTP/3 requer QUIC, o QUIC também pode ser usado com outros protocolos. Um bom exemplo é o DNS sobre QUIC, conforme definido na RFC 9250, DNS sobre conexões QUIC dedicadas.)

Feita essa introdução, vamos nos aprofundar em alguns detalhes específicos da rede QUIC.

TCP versus UDP

O QUIC introduz uma mudança significativa no protocolo de rede subjacente usado para transmitir dados de aplicações HTTP entre um cliente e um servidor.

Como mencionado, o TCP sempre foi o protocolo usado para transmitir dados de aplicações web HTTP. O TCP foi projetado para entregar dados de forma confiável por meio de uma rede IP. Ele conta com um mecanismo bem definido e amplamente compreendido para estabelecer conexões e confirmar o recebimento de dados, além de diversos algoritmos e técnicas para gerenciar a perda e o atraso de pacotes, comuns em redes não confiáveis e congestionadas.

Embora o TCP ofereça um transporte confiável, há algumas desvantagens em termos de desempenho e latência. Além disso, a criptografia de dados não é integrada ao TCP e precisa ser implementada separadamente. Também tem sido difícil aprimorar ou ampliar o TCP diante das mudanças nos padrões de tráfego HTTP, pois o processamento do TCP é realizado no kernel do Linux, e qualquer alteração precisa ser cuidadosamente projetada e testada para evitar efeitos imprevistos no desempenho e na estabilidade geral do sistema.

Outro problema é que, em muitos cenários, o tráfego HTTP entre o cliente e o servidor passa por vários dispositivos que processam TCP, como firewalls ou balanceadores de carga (conhecidos coletivamente como “middleboxes”), que podem demorar a implementar alterações nos padrões TCP.

O QUIC, por sua vez, usa o UDP como protocolo de transporte. O UDP foi projetado para transmitir dados por uma rede IP, assim como o TCP, mas dispensa intencionalmente o estabelecimento de conexão e a entrega confiável. Essa ausência de sobrecarga torna o UDP adequado para muitas aplicações em que a eficiência e a velocidade são mais importantes do que a confiabilidade.

No entanto, para a maioria das aplicações web, a entrega confiável de dados é essencial. Como a camada de transporte UDP subjacente não oferece entrega confiável de dados, essas funções precisam ser fornecidas pelo QUIC (ou pela própria aplicação). Felizmente, o QUIC tem algumas vantagens sobre o TCP nesse aspecto:

  • O processamento do QUIC é realizado no espaço do usuário do Linux, onde problemas com uma operação específica representam menos riscos para o sistema como um todo. Isso torna mais viável o desenvolvimento rápido de novos recursos.
  • As “middleboxes” mencionadas acima geralmente realizam um processamento mínimo do tráfego UDP e, portanto, não limitam as melhorias no protocolo QUIC.

Anatomia simplificada de uma rede QUIC

QUIC streams são os objetos lógicos que contêm solicitações ou respostas HTTP/3 (ou quaisquer outros dados de aplicação). Para a transmissão entre endpoints de rede, eles são encapsulados em várias camadas lógicas, conforme ilustrado no diagrama.

Figura 1. Anatomia de um fluxo QUIC

De fora para dentro, as camadas e os objetos lógicos são:

  • Datagrama UDP – Contém um cabeçalho que especifica as portas de origem e destino (além dos dados de comprimento e soma de verificação), seguido por um ou mais pacotes QUIC. O datagrama é a unidade de informação transmitida do cliente para o servidor pela rede.
  • Pacote QUIC – Contém um cabeçalho QUIC e um ou mais quadros QUIC.
  • Cabeçalho QUIC – Contém metadados sobre o pacote. Há dois tipos de cabeçalho:
    • O cabeçalho longo, usado durante o estabelecimento da conexão.
    • O cabeçalho curto, usado após o estabelecimento da conexão. Ele contém, entre outros dados, o ID da conexão, o número do pacote e a fase da chave (usada para identificar quais chaves foram utilizadas para criptografar o pacote, permitindo a rotação de chaves). Os números dos pacotes são exclusivos (e sempre crescentes) para uma determinada conexão e fase da chave.
  • Quadro – Contém o tipo, o ID do fluxo, o deslocamento e os dados do fluxo. Os dados do fluxo são distribuídos entre vários quadros, mas podem ser reunidos usando o ID da conexão, o ID do fluxo e o deslocamento, que é usado para apresentar os blocos de dados na ordem correta.
  • Fluxo – Um fluxo de dados unidirecional ou bidirecional dentro de uma única conexão QUIC. Cada conexão QUIC pode comportar vários fluxos independentes, cada um com seu próprio ID de fluxo. Se um pacote QUIC contendo alguns fluxos for perdido, isso não afetará o andamento dos fluxos que não estiverem contidos no pacote perdido (isso é essencial para evitar o bloqueio de início de fila observado no HTTP/2). Os fluxos podem ser bidirecionais e criados por qualquer um dos endpoints.

Estabelecimento da conexão

O conhecido handshake de três vias SYN / SYN-ACK / ACK estabelece uma conexão TCP:

Figura 2. O handshake de três vias que estabelece uma conexão TCP

O estabelecimento de uma conexão QUIC envolve etapas semelhantes, mas é mais eficiente. Ele também incorpora a validação de endereço à configuração da conexão como parte do handshake criptográfico. A validação de endereço protege contra ataques de amplificação de tráfego, nos quais um agente mal-intencionado envia ao servidor um pacote com informações de endereço de origem falsificadas para a vítima pretendida. O invasor espera que o servidor gere mais pacotes ou pacotes maiores para a vítima do que ele próprio conseguiria gerar, resultando em um volume excessivo de tráfego. (Para mais detalhes, consulte a Seção 8 da RFC 9000, QUIC: um transporte seguro e multiplexado baseado em UDP.)

Como parte do estabelecimento da conexão, o cliente e o servidor fornecem IDs de conexão independentes, que são codificados no cabeçalho QUIC, permitindo identificar facilmente a conexão, independentemente do endereço IP de origem do cliente.

No entanto, como o estabelecimento inicial de uma conexão QUIC também inclui operações de troca de chaves de criptografia TLS, ele exige mais recursos computacionais do servidor do que a simples resposta SYN-ACK gerada durante o estabelecimento de uma conexão TCP. Isso também cria um possível vetor para ataques de negação de serviço distribuída (DDoS), pois o endereço IP do cliente não é validado antes da realização das operações de troca de chaves.

Mas você pode configurar o NGINX para validar o endereço IP do cliente antes do início de operações criptográficas complexas, definindo a diretiva quic_retry como on. Nesse caso, o NGINX envia ao cliente um pacote de repetição contendo um token, que o cliente deve incluir nos pacotes de estabelecimento da conexão.

Figura 3. Estabelecimento de conexão QUIC, sem e com um pacote de repetição

Esse mecanismo é semelhante ao handshake de três vias do TCP e, sobretudo, confirma que o cliente é o proprietário do endereço IP de origem que está apresentando. Sem essa verificação, servidores QUIC como o NGINX poderiam ficar vulneráveis a ataques de DoS simples, realizados com endereços IP de origem falsificados. (Outro mecanismo do QUIC que reduz o risco desses ataques é a exigência de que todos os pacotes iniciais de conexão sejam preenchidos até atingir um mínimo de 1200 bytes, tornando seu envio uma operação mais custosa.)

Além disso, os pacotes de repetição mitigam um ataque semelhante ao ataque de inundação SYN do TCP (no qual os recursos do servidor são esgotados por um grande número de handshakes iniciados, mas não concluídos, armazenados na memória), codificando os detalhes da conexão no ID de conexão enviado ao cliente. Isso oferece ainda a vantagem de dispensar a retenção de informações no servidor, pois os dados da conexão podem ser reconstituídos a partir do ID de conexão e do token posteriormente apresentados pelo cliente. Essa técnica é análoga aos cookies SYN do TCP. Além disso, servidores QUIC como o NGINX podem fornecer um token com prazo de validade para ser usado em futuras conexões do cliente, acelerando a retomada da conexão.

O uso de IDs de conexão permite que a conexão seja independente da camada de transporte subjacente, de modo que alterações na rede não necessariamente interrompam as conexões. Esse assunto é abordado em Gerenciamento transparente de alterações no endereço IP do cliente.

Detecção de perdas

Com uma conexão estabelecida (e a criptografia habilitada, conforme discutido mais adiante abaixo), as solicitações e respostas HTTP podem fluir nos dois sentidos entre o cliente e o NGINX. Datagramas UDP são enviados e recebidos. No entanto, há muitos fatores que podem fazer com que alguns desses datagramas sejam perdidos ou sofram atrasos.

O TCP possui mecanismos complexos para confirmar a entrega de pacotes, detectar a perda ou o atraso de pacotes e gerenciar a retransmissão de pacotes perdidos, fornecendo dados completos e na sequência correta à camada de aplicação. O UDP não dispõe desse recurso e, portanto, o controle de congestionamento e a detecção de perdas são implementados na camada QUIC.

  • Tanto o cliente quanto o servidor enviam uma confirmação explícita para cada pacote QUIC recebido (embora os pacotes que contenham apenas quadros de baixa prioridade não sejam confirmados imediatamente).
  • Quando um pacote contendo quadros que exigem entrega confiável não é confirmado após um período de tempo limite definido, ele é considerado perdido. Os períodos de tempo limite variam de acordo com o conteúdo do pacote – por exemplo, o tempo limite é menor para pacotes necessários para estabelecer a criptografia e configurar a conexão, pois eles são essenciais para o desempenho do handshake do QUIC.
  • Quando um pacote é considerado perdido, os frames ausentes são retransmitidos em um novo pacote, que recebe um novo número de sequência.
  • O destinatário do pacote usa o ID do fluxo e o deslocamento nos pacotes para organizar os dados transmitidos na ordem correta. O número do pacote determina apenas a ordem de envio, não como os pacotes devem ser organizados.
  • Como a montagem dos dados no receptor independe da ordem de transmissão, um pacote perdido ou atrasado afeta apenas os fluxos individuais que ele contém, e não todos os fluxos da conexão. Isso elimina o problema de bloqueio de início de fila que afeta o HTTP/1.x e o HTTP/2, pois os fluxos não fazem parte da camada de transporte.

Uma descrição completa da detecção de perdas está além do escopo deste guia introdutório. Consulte a RFC 9002, Detecção de perdas e controle de congestionamento do QUIC, para obter detalhes sobre os mecanismos usados para determinar os tempos limite e a quantidade de dados não confirmados que pode permanecer em trânsito.

Gerenciamento harmonioso de alterações no endereço IP do cliente

O endereço IP de um cliente (chamado de endereço IP de origem no contexto de uma sessão de aplicativo) pode mudar durante a sessão, por exemplo, quando uma VPN ou um gateway altera seu endereço público ou quando o usuário de um smartphone sai de uma área com cobertura Wi-Fi, forçando a mudança para uma rede celular. Além disso, os administradores de rede tradicionalmente definem tempos limite menores para o tráfego UDP do que para conexões TCP, o que aumenta a probabilidade de revinculação da tradução de endereços de rede (NAT).

O QUIC oferece dois mecanismos para reduzir a interrupção que pode ocorrer: um cliente pode informar proativamente ao servidor que seu endereço será alterado, e os servidores podem lidar de forma transparente com uma alteração não planejada no endereço do cliente. Como o ID da conexão permanece o mesmo durante a transição, os quadros não confirmados podem ser retransmitidos para o novo endereço IP.

Alterações no endereço IP de origem durante sessões QUIC podem representar um problema para balanceadores de carga downstream (ou outros componentes de rede da Camada 4) que usam o endereço IP e a porta de origem para determinar qual servidor upstream deve receber um determinado datagrama UDP. Para garantir o gerenciamento correto do tráfego, os fornecedores de dispositivos de rede da Camada 4 precisarão atualizá-los para que processem IDs de conexão QUIC. Para saber mais sobre o futuro do balanceamento de carga e do QUIC, consulte o rascunho da IETF QUIC‑LB: geração de IDs de conexão QUIC roteáveis.

Criptografia

Em Estabelecimento da conexão, mencionamos que o handshake inicial do QUIC faz mais do que simplesmente estabelecer uma conexão. Ao contrário do handshake TLS para TCP, com UDP a troca de chaves e de parâmetros de criptografia do TLS 1.3 ocorre como parte da conexão inicial. Esse recurso elimina várias trocas e permite um tempo de ida e volta zero (0‑RTT) quando o cliente retoma uma conexão anterior.

Figura 4. Comparação dos handshakes de criptografia do TCP+TLS/1.3 e do QUIC

Além de incorporar o handshake de criptografia ao processo de estabelecimento da conexão, o QUIC criptografa uma parcela maior dos metadados do que o TCP+TLS. Mesmo antes da troca de chaves, os pacotes iniciais da conexão são criptografados; embora um interceptador ainda possa obter as chaves, isso exige mais esforço do que no caso de pacotes não criptografados. Isso protege melhor dados como a Indicação do Nome do Servidor (SNI), que é relevante tanto para invasores quanto para possíveis censores estatais. A Figura 5 ilustra como o QUIC criptografa mais metadados potencialmente sensíveis (em vermelho) do que o TCP+TLS.

Figura 5. O QUIC criptografa mais metadados sensíveis do que TCP+TLS

Todos os dados na carga útil do QUIC são criptografados usando TLS 1.3. Isso oferece duas vantagens: conjuntos de cifras e algoritmos de hash antigos e vulneráveis não são permitidos, e mecanismos de troca de chaves com sigilo encaminhado (FS) são obrigatórios. Sigilo encaminhado impede que um invasor descriptografe os dados, mesmo que obtenha a chave privada e uma cópia do tráfego.

Conexões de RTT baixo e zero reduzem a latência

Reduzir o número de viagens de ida e volta necessárias entre um cliente e um servidor antes que qualquer dado da aplicação possa ser transmitido melhora o desempenho das aplicações, especialmente em redes com maior latência.

O TLS 1.3 introduziu uma única viagem de ida e volta para estabelecer uma conexão criptografada e nenhuma viagem de ida e volta para retomar uma conexão. No entanto, com o TCP, isso significa que o handshake precisa ocorrer antes do TLS Client Hello.

Como o QUIC combina operações criptográficas com o estabelecimento da conexão, ele permite um verdadeiro restabelecimento de conexão com 0‑RTT, no qual um cliente pode enviar uma solicitação já no primeiro pacote QUIC. Isso reduz a latência ao eliminar a troca inicial necessária para estabelecer a conexão antes da primeira solicitação.

Figura 6. Comparação das mensagens necessárias para restabelecer uma conexão com TCP+TLS e com QUIC

Nesse caso, o cliente envia uma solicitação HTTP criptografada com os parâmetros usados em uma conexão anterior e, para fins de validação de endereço, inclui um token fornecido pelo servidor durante a conexão anterior.

Infelizmente, a retomada de conexão 0‑RTT não oferece sigilo encaminhado, portanto, a solicitação inicial do cliente não é criptografada com o mesmo nível de segurança que o restante do tráfego na comunicação. As solicitações e respostas posteriores à primeira solicitação são protegidas por sigilo encaminhado. Um problema possivelmente mais grave é que a solicitação inicial também é vulnerável a ataques de repetição, nos quais um invasor pode capturar a solicitação inicial e reenviá-la ao servidor várias vezes.

Para muitos aplicativos e sites, o ganho de desempenho proporcionado pela retomada de conexão 0‑RTT compensa essas possíveis vulnerabilidades, mas cabe a você tomar essa decisão.

Esse recurso vem desabilitado por padrão no NGINX. Para habilitá-lo, defina a diretiva ssl_early_data como on.

Migrando do HTTP/1.1 para o HTTP/3 com o cabeçalho Alt-Svc Header

Quase todos os clientes (especialmente os navegadores) estabelecem as conexões iniciais por TCP/TLS. Se um servidor for compatível com QUIC+HTTP/3, ele informa isso ao cliente retornando uma resposta HTTP/1.1 que inclui o parâmetro h3 no cabeçalho Alt-Svc. O cliente então decide se usará QUIC+HTTP/3 ou se continuará usando uma versão anterior do HTTP. (A título de curiosidade, o cabeçalho Alt-Svc, definido na RFC 7838, é anterior ao QUIC e também pode ser usado para outras finalidades.)

Figura 7. Como o cabeçalho Alt-Svc é usado para converter uma conexão de HTTP/1.1 para HTTP/3

O cabeçalho Alt-Svc informa ao cliente que o mesmo serviço está disponível em um host, protocolo ou porta alternativos (ou em uma combinação deles). Além disso, os clientes podem ser informados por quanto tempo é seguro presumir que esse serviço continuará disponível.

Alguns exemplos:

Alt-Svc: h3=":443" O HTTP/3 está disponível neste servidor na porta 443Alt-Svc: h3="new.example.com:8443" O HTTP/3 está disponível no servidor new.example.com na porta 8443Alt-Svc: h3=":8443"; ma=600 O HTTP/3 está disponível neste servidor na porta 8443 e permanecerá disponível por pelo menos 10 minutos

Embora não seja obrigatório, na maioria dos casos os servidores são configurados para responder a conexões QUIC na mesma porta que TCP+TLS.

Para configurar o NGINX para incluir o cabeçalho Alt-Svc, use a diretiva add_header. Neste exemplo, a variável $server_port significa que o NGINX aceita conexões QUIC na porta para a qual o cliente enviou sua solicitação TCP+TLS, e 86.400 corresponde a 24 horas:

add_header Alt-Svc 'h3=":$server_port"; ma=86400';

Conclusão

Este blog apresenta uma introdução simplificada ao QUIC e, esperamos, oferece uma visão geral suficiente para você entender as principais operações de rede e criptografia usadas com o QUIC.

Para uma visão mais abrangente sobre como configurar o NGINX para QUIC + HTTP/3, leia Pacotes binários já disponíveis para a implementação de prévia do NGINX QUIC+HTTP/3 em nosso blog ou assista ao nosso webinar, Experimente o NGINX e o QUIC+HTTP/3 na prática. Para obter detalhes sobre todas as diretivas do NGINX para QUIC+HTTP/3 e instruções completas para instalar binários pré-compilados ou compilar a partir do código-fonte, consulte a página do NGINX QUIC.


Compartilhar
Tags: Tech, F5 NGINX

Sobre o autor

Publicações relacionadas no blog

Como o F5 NGINX One aprimora o gerenciamento e a observabilidade
NGINX | 10/10/2024

Como o F5 NGINX One aprimora o gerenciamento e a observabilidade

Descubra como o F5 NGINX One aprimora o gerenciamento e a observabilidade de ambientes híbridos e multicloud ao oferecer um console web centralizado baseado em SaaS, com insights em tempo real, conformidade automatizada e gerenciamento simplificado de configurações.

Do OpenAPI ao NGINX como gateway de API usando uma API declarativa
NGINX | 17/06/2024

Do OpenAPI ao NGINX como gateway de API usando uma API declarativa

Descubra como transformar uma definição de esquema OpenAPI em uma configuração NGINX totalmente funcional, operando como um API Gateway com a segurança de um Web Application Firewall e um Portal do Desenvolvedor, usando uma abordagem declarativa de API.

Como gerenciar picos repentinos de tráfego e sobrecarga do servidor
NGINX | 14/07/2021

Como gerenciar picos repentinos de tráfego e sobrecarga do servidor

Picos de tráfego e sobrecarga de servidores são preocupações sérias para varejistas online em qualquer época, mas especialmente durante a “Cyber Season” de compras de fim de ano. Detalhamos como você pode aproveitar as soluções de entrega de aplicações de nível de produção da NGINX para se preparar para aumentos repentinos de tráfego.

Distribuição segura de chaves privadas SSL com o NGINX
NGINX | 02/04/2019

Distribuição segura de chaves privadas SSL com o NGINX

Descrevemos três maneiras progressivamente mais seguras de proteger chaves privadas SSL ao configurar o NGINX para processar tráfego HTTPS: permitir acesso de leitura apenas ao usuário root, criptografar as chaves com senhas armazenadas separadamente e distribuir as senhas a partir de um repositório central.

Agrupando solicitações de API em lotes com NGINX Plus e o módulo JavaScript do NGINX
NGINX | 24/05/2018

Agrupando solicitações de API em lotes com NGINX Plus e o módulo JavaScript do NGINX

O módulo JavaScript do NGINX lançado com o NGINX Plus R15 oferece suporte a subsolicitações, possibilitando uma classe totalmente nova de casos de uso para o NGINX Plus. Este exemplo completo também usa a API do NGINX Plus e os módulos de chave-valor para converter solicitações de API dos clientes em solicitações separadas para as APIs dos microsserviços que compõem um aplicativo.

Como superar o esgotamento de portas efêmeras no NGINX e no NGINX Plus
NGINX | 19/04/2016

Como superar o esgotamento de portas efêmeras no NGINX e no NGINX Plus

Confira dicas para evitar o esgotamento de portas efêmeras com ajustes no kernel do Linux, conexões keepalive e conexões vinculadas dinamicamente a endereços IP locais.