Como proteger seu site público com ESNI

Olá Habr, meu nome é Ilya, trabalho na equipe de plataforma da Exness. Desenvolvemos e implementamos os principais componentes de infraestrutura usados ​​por nossas equipes de desenvolvimento de produtos.

Neste artigo, gostaria de compartilhar minha experiência de implementação da tecnologia SNI criptografada (ESNI) na infraestrutura de sites públicos.

Como proteger seu site público com ESNI

A utilização desta tecnologia aumentará o nível de segurança ao trabalhar com um site público e atenderá aos padrões internos de segurança adotados pela Empresa.

Em primeiro lugar, gostaria de salientar que a tecnologia não está padronizada e ainda está em fase de rascunho, mas CloudFlare e Mozilla já a suportam (em rascunho01). Isso nos motivou para tal experimento.

Um pouco de teoria

ESNI é uma extensão do protocolo TLS 1.3 que permite a criptografia SNI na mensagem TLS handshake "Client Hello". Esta é a aparência do Client Hello com suporte ESNI (em vez do SNI normal, vemos ESNI):

Como proteger seu site público com ESNI

 Para usar ESNI, você precisa de três componentes:

  • DNS; 
  • Suporte ao cliente;
  • Suporte do lado do servidor.

DNS

Você precisa adicionar dois registros DNS – AE SMS (O registro TXT contém a chave pública com a qual o cliente pode criptografar o SNI) - veja abaixo. Além disso, deve haver apoio DoH (DNS sobre HTTPS) porque os clientes disponíveis (veja abaixo) não habilitam o suporte ESNI sem DoH. Isso é lógico, pois ESNI implica criptografia do nome do recurso que estamos acessando, ou seja, não faz sentido acessar DNS por UDP. Além disso, o uso DNSSEC permite proteger contra ataques de envenenamento de cache neste cenário.

Disponível atualmente vários provedores de DoH, entre eles:

CloudFlare declara (Verifique Meu Navegador → SNI Criptografado → Saiba Mais) que seus servidores já suportam ESNI, ou seja, para servidores CloudFlare no DNS temos pelo menos dois registros – A e TXT. No exemplo abaixo consultamos o DNS do Google (por HTTPS): 

А registro:

curl 'https://dns.google.com/resolve?name=www.cloudflare.com&type=A' 
-s -H 'accept: application/dns+json'
{
  "Status": 0,
  "TC": false,
  "RD": true,
  "RA": true,
  "AD": true,
  "CD": false,
  "Question": [
    {
      "name": "www.cloudflare.com.",
      "type": 1
    }
  ],
  "Answer": [
    {
      "name": "www.cloudflare.com.",
      "type": 1,
      "TTL": 257,
      "data": "104.17.210.9"
    },
    {
      "name": "www.cloudflare.com.",
      "type": 1,
      "TTL": 257,
      "data": "104.17.209.9"
    }
  ]
}

SMS registro, a solicitação é gerada de acordo com um modelo _esni.FQDN:

curl 'https://dns.google.com/resolve?name=_esni.www.cloudflare.com&type=TXT' 
-s -H 'accept: application/dns+json'
{
  "Status": 0,
  "TC": false,
  "RD": true,
  "RA": true,
  "AD": true,
  "CD": false,
  "Question": [
    {
    "name": "_esni.www.cloudflare.com.",
    "type": 16
    }
  ],
  "Answer": [
    {
    "name": "_esni.www.cloudflare.com.",
    "type": 16,
    "TTL": 1799,
    "data": ""/wEUgUKlACQAHQAg9SiAYQ9aUseUZr47HYHvF5jkt3aZ5802eAMJPhRz1QgAAhMBAQQAAAAAXtUmAAAAAABe3Q8AAAA=""
    }
  ],
  "Comment": "Response from 2400:cb00:2049:1::a29f:209."
}

Portanto, do ponto de vista do DNS, devemos usar DoH (de preferência com DNSSEC) e adicionar duas entradas. 

Suporte ao cliente

Se estamos falando de navegadores, então no momento o suporte é implementado apenas no FireFox. é Aqui estão instruções sobre como ativar o suporte ESNI e DoH no FireFox. Após a configuração do navegador, devemos ver algo assim:

Como proteger seu site público com ESNI

Link para verificar o navegador.

É claro que o TLS 1.3 deve ser usado para oferecer suporte ao ESNI, já que o ESNI é uma extensão do TLS 1.3.

Para testar o backend com suporte ESNI, implementamos o cliente em go, Mas mais sobre isso mais tarde.

Suporte do lado do servidor

Atualmente, ESNI não é suportado por servidores web como nginx/apache, etc., uma vez que funcionam com TLS via OpenSSL/BoringSSL, que não suportam oficialmente ESNI.

Portanto, decidimos criar nosso próprio componente front-end (proxy reverso ESNI), que suportaria terminação TLS 1.3 com ESNI e tráfego proxy HTTP(S) para o upstream, que não suporta ESNI. Isso permite que a tecnologia seja utilizada em uma infraestrutura já existente, sem alterar os componentes principais – ou seja, utilizando servidores web atuais que não suportam ESNI. 

Para maior clareza, aqui está um diagrama:

Como proteger seu site público com ESNI

Observo que o proxy foi projetado com a capacidade de encerrar uma conexão TLS sem ESNI, para oferecer suporte a clientes sem ESNI. Além disso, o protocolo de comunicação com o upstream pode ser HTTP ou HTTPS com uma versão TLS inferior a 1.3 (se o upstream não suportar 1.3). Este esquema oferece flexibilidade máxima.

Implementação do suporte ESNI em go nós pegamos emprestado de CloudFlare. Gostaria de observar desde já que a implementação em si não é nada trivial, pois envolve mudanças na biblioteca padrão cripto / tls e, portanto, requer “remendo” GOROOT antes da montagem.

Para gerar chaves ESNI usamos esnitool (também ideia da CloudFlare). Essas chaves são usadas para criptografia/descriptografia SNI.
Testamos a compilação usando go 1.13 no Linux (Debian, Alpine) e MacOS. 

Algumas palavras sobre recursos operacionais

O proxy reverso ESNI fornece métricas no formato Prometheus, como rps, latência upstream e códigos de resposta, handshakes TLS com falha/bem-sucedidos e duração do handshake TLS. À primeira vista, isso parecia suficiente para avaliar como o proxy lida com o tráfego. 

Também realizamos testes de carga antes do uso. Resultados abaixo:

wrk -t50 -c1000 -d360s 'https://esni-rev-proxy.npw:443' --timeout 15s
Running 6m test @ https://esni-rev-proxy.npw:443
  50 threads and 1000 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     1.77s     1.21s    7.20s    65.43%
    Req/Sec    13.78      8.84   140.00     83.70%
  206357 requests in 6.00m, 6.08GB read
Requests/sec:    573.07
Transfer/sec:     17.28MB 

Realizamos testes de carga puramente qualitativos para comparar o esquema usando e sem proxy reverso ESNI. “Despejamos” o tráfego localmente para eliminar “interferências” em componentes intermediários.

Portanto, com suporte ESNI e proxy para upstream de HTTP, recebemos cerca de aproximadamente 550 rps de uma instância, com o consumo médio de CPU/RAM do proxy reverso ESNI:

  • 80% de uso de CPU (4 vCPU, hosts de 4 GB de RAM, Linux)
  • 130 MB de memória RSS

Como proteger seu site público com ESNI

Para efeito de comparação, o RPS para o mesmo upstream nginx sem terminação TLS (protocolo HTTP) é ~ 1100:

wrk -t50 -c1000 -d360s 'http://lb.npw:80' –-timeout 15s
Running 6m test @ http://lb.npw:80
  50 threads and 1000 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     1.11s     2.30s   15.00s    90.94%
    Req/Sec    23.25     13.55   282.00     79.25%
  393093 requests in 6.00m, 11.35GB read
  Socket errors: connect 0, read 0, write 0, timeout 9555
  Non-2xx or 3xx responses: 8111
Requests/sec:   1091.62
Transfer/sec:     32.27MB 

A presença de timeouts indica que há falta de recursos (usamos 4 vCPUs, hosts de 4 GB de RAM, Linux) e, de fato, o RPS potencial é maior (recebemos números de até 2700 RPS em recursos mais potentes).

Concluindo, observo que a tecnologia ESNI parece bastante promissora. Ainda há muitas questões em aberto, por exemplo, as questões de armazenamento da chave pública ESNI no DNS e rotação de chaves ESNI - essas questões estão sendo discutidas ativamente, e a versão mais recente do rascunho ESNI (no momento da redação) já está 7.

Fonte: habr.com

Adicionar um comentário