Wie man seine öffentliche Website mit ESNI schützt

Hallo Habr, ich heiße Ilja, ich arbeite im Plattformteam der Firma Exness. Wir entwickeln und implementieren grundlegende Infrastrukturelemente, die von unseren Produktentwicklungsteams genutzt werden.

In diesem Artikel möchte ich meine Erfahrungen bei der Implementierung der Technologie encrypted SNI (ESNI) in der Infrastruktur öffentlicher Webseiten teilen.

Wie man seine öffentliche Website mit ESNI schützt

Die Verwendung dieser Technologie wird das Sicherheitsniveau bei der Arbeit mit öffentlichen Webseiten erhöhen und den internen Sicherheitsstandards der Firma entsprechen.

Zunächst möchte ich darauf hinweisen, dass die Technologie nicht standardisiert ist und sich noch im Entwurf befindet. Allerdings unterstützen CloudFlare und Mozilla sie bereits (im draft01). Das hat uns zu diesem Experiment motiviert.

Ein wenig Theorie

ESNI ist eine Erweiterung des TLS 1.3 Protokolls, die es ermöglicht, SNI in der 'Client Hello'-Nachricht des TLS-Handshake zu verschlüsseln. So sieht ein Client Hello mit ESNI-Unterstützung aus (statt des üblichen SNI sehen wir ESNI):

Wie man seine öffentliche Website mit ESNI schützt

 Um ESNI zu verwenden, sind drei Komponenten erforderlich:

  • DNS; 
  • Unterstützung seitens des Clients;
  • Unterstützung seitens des Servers.

DNS

Es müssen zwei DNS-Einträge hinzugefügt werden – A, und TXT (Der TXT-Eintrag enthält den öffentlichen Schlüssel, mit dem der Client SNI verschlüsseln kann.) – siehe unten. Außerdem muss die Unterstützung für DoH (DNS über HTTPS) gegeben sein, da die verfügbaren Clients (siehe unten) die ESNI-Unterstützung ohne DoH nicht aktivieren. Das ist sinnvoll, da ESNI eine Verschlüsselung des Ressourcennamens impliziert, auf den wir zugreifen, was bedeutet, dass es sinnlos ist, DNS über UDP anzusprechen. Darüber hinaus ermöglicht die Verwendung von DNSSEC den Schutz vor „Cache Poisoning“-Angriffen in diesem Szenario.

Derzeit stehen mehrere DoH-Anbieter, darunter:

CloudFlare erklärt, (Check My Browser → Encrypted SNI → Learn More), deren Server bereits jetzt ESNI unterstützen, was bedeutet, dass wir für CloudFlare-Server im DNS mindestens zwei Einträge haben – A und TXT. Im folgenden Beispiel fordern wir Google DNS (über HTTPS) an: 

A Eintrag:

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"
    }
  ]
}

TXT Eintrag, die Anfrage wird gemäß der Vorlage _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": "Antwort von 2400:cb00:2049:1::a29f:209."
}

Aus Sicht von DNS sollten wir DoH (vorzugsweise mit DNSSEC) verwenden und zwei Einträge hinzufügen. 

Unterstützung durch den Client

Wenn wir über Browser sprechen, ist derzeit die Unterstützung nur in FireFox implementiert.. Hier Hier ist eine Anleitung, wie Sie die Unterstützung für ESNI und DoH in FireFox aktivieren können. Nachdem der Browser konfiguriert ist, sollten wir ungefähr folgendes Bild sehen:

Wie man seine öffentliche Website mit ESNI schützt

Link zur Überprüfung des Browsers.

Natürlich muss TLS 1.3 zur Unterstützung von ESNI verwendet werden, da ESNI eine Erweiterung von TLS 1.3 ist.

Für Testzwecke des Backends mit ESNI-Unterstützung haben wir einen Client auf go, aber dazu später mehr.

Unterstützung durch den Server

Aktuell wird ESNI von Webservern wie nginx/apache usw. nicht unterstützt, da sie mit TLS über OpenSSL/BoringSSL arbeiten, in denen ESNI offiziell nicht unterstützt wird.

Deshalb haben wir beschlossen, ein eigenes Front-End-Komponent (ESNI-Proxy) zu erstellen, das die Terminierung von TLS 1.3 mit ESNI und das Proxying von HTTP(S)-Verkehr an ein Upstream, das ESNI nicht unterstützt, ermöglicht. Dies erlaubt den Einsatz der Technologie in bestehenden Infrastrukturen, ohne die grundlegenden Komponenten – also die aktuellen Webserver, die ESNI nicht unterstützen – zu verändern. 

Zur Veranschaulichung hier ein Diagramm:

Wie man seine öffentliche Website mit ESNI schützt

Ich möchte darauf hinweisen, dass das Proxy so konzipiert wurde, dass es die TLS-Verbindung ohne ESNI terminieren kann, um Clients ohne ESNI zu unterstützen. Auch das Kommunikationsprotokoll mit dem Upstream kann sowohl HTTP als auch HTTPS mit einer TLS-Version unter 1.3 sein (wenn das Upstream 1.3 nicht unterstützt). Dieses Schema bietet maximale Flexibilität.

Wir haben die Unterstützung für ESNI auf go von CloudFlareübernommen. Ich möchte gleich darauf hinweisen, dass die Implementierung selbst recht komplex ist, da sie Änderungen an der Standardbibliothek crypto/tls erfordert und daher ein 'Patching' GOROOT vor dem Build benötigt.

Für die Generierung von ESNI-Schlüsseln haben wir esnitool verwendet (auch ein Produkt von CloudFlare). Diese Schlüssel werden für die Verschlüsselung/Dekodierung von SNI verwendet.
Wir haben den Build mit Go 1.13 auf Linux (Debian, Alpine) und MacOS getestet. 

Ein paar Worte zu betriebswirtschaftlichen Besonderheiten

Der ESNI-Reverse-Proxy stellt Metriken im Prometheus-Format bereit, wie zum Beispiel rps, Upstream-Latenz & Antwortcodes, fehlgeschlagene/erfolgreiche TLS-Handshakes & TLS-Handshake-Dauer. Auf den ersten Blick schien dies ausreichend zu sein, um zu bewerten, wie der Proxy mit dem Traffic umgeht. 

Außerdem haben wir vor der Nutzung Lasttests durchgeführt. Die Ergebnisse sind unten aufgeführt:

wrk -t50 -c1000 -d360s 'https://esni-rev-proxy.npw:443' --timeout 15s
Ausführen des 6-minütigen Tests @ https://esni-rev-proxy.npw:443
  50 Threads und 1000 Verbindungen
  Thread-Statistiken   Avg      Stdev     Max   +/− Stdev
    Latenz     1.77s     1.21s    7.20s    65.43%
    Anfragen/Sek.    13.78      8.84   140.00     83.70%
  206357 Anfragen in 6.00m, 6.08GB gelesen
Anfragen/Sek:    573.07
Übertragung/Sek:     17.28MB 

Die Lasttests haben wir rein qualitativ durchgeführt, um die Szenarien mit und ohne ESNI-Reverse-Proxy zu vergleichen. Wir „haben“ Traffic lokal bereitgestellt, um „Störungen“ in den Zwischenkomponenten auszuschließen.

Mit Unterstützung von ESNI und dem Proxying zum Upstream über HTTP erhielten wir etwa ~ 550 rps von einer Instanz, wobei der durchschnittliche CPU-/RAM-Verbrauch des ESNI-Reverse-Proxys beträgt:

  • 80% CPU-Auslastung (4 vCPU, 4 GB RAM-Hosts, Linux)
  • 130 MB Mem RSS

Wie man seine öffentliche Website mit ESNI schützt

Zum Vergleich, RPS für denselben Upstream von Nginx ohne TLS-Terminierung (HTTP-Protokoll) ~ 1100:

wrk -t50 -c1000 -d360s 'http://lb.npw:80' --timeout 15s
Ausführen des 6-minütigen Tests @ http://lb.npw:80
  50 Threads und 1000 Verbindungen
  Thread-Statistiken   Avg      Stdev     Max   +/− Stdev
    Latenz     1.11s     2.30s   15.00s    90.94%
    Anfragen/Sek.    23.25     13.55   282.00     79.25%
  393093 Anfragen in 6.00m, 11.35GB gelesen
  Socket-Fehler: verbinden 0, lesen 0, schreiben 0, Zeitüberschreitung 9555
  Nicht-2xx oder 3xx Antworten: 8111
Anfragen/Sek:   1091.62
Übertragung/Sek:     32.27MB 

Das Vorhandensein von Zeitüberschreitungen deutet darauf hin, dass es an Ressourcen mangelt (wir haben 4 vCPU, 4 GB RAM-Hosts, Linux verwendet), und tatsächlich ist das potenzielle RPS höher (wir erhielten Zahlen bis 2700 RPS auf leistungsfähigeren Ressourcen).

Abschließend möchte ich betonen, dass die ESNI-Technologie vielversprechend aussieht. Es gibt noch viele offene Fragen, wie zum Beispiel die Speicherung des öffentlichen ESNI-Schlüssels in DNS und das Rotieren von ESNI-Schlüsseln – diese Fragen werden intensiv diskutiert, und die letzte Version des Entwurfs (zum Zeitpunkt des Schreibens) von ESNI ist bereits 7.

Quelle: habr.com

60GB SSD 8Gb DDR4