Was TLS bei einer HTTPS-Verbindung tatsächlich schützt
Laden Sie eine Seite über reines http:// in einem Netz, das Sie nicht kontrollieren, an einem Flughafen oder über den Router eines Cafés, dann läuft alles, was Sie senden, als lesbarer Text mit. Der URL-Pfad, die Cookies, die Formularfelder, das Passwort in einem Login-POST: Jeder in diesem Netz mit einem Paket-Sniffer liest es im Vorbeigehen. TLS, Transport Layer Security, verhindert genau das. Es sitzt zwischen TCP und dem Anwendungsprotokoll, sodass die Verbindung bereits verschlüsselt und an eine verifizierte Server-Identität gebunden ist, wenn HTTP zu sprechen beginnt. HTTPS ist HTTP, das über diese Verbindung läuft statt über eine unverschlüsselte.
TLS bündelt drei Garantien, und sie gelten nur zusammen. Verschlüsselung bedeutet, dass ein Beobachter im Netz nur Chiffretext sieht, sonst nichts. Integrität bedeutet, dass ein während der Übertragung gekipptes Bit erkannt wird und die Verbindung abbricht, statt veränderte Daten auszuliefern. Authentifizierung bedeutet, dass der Client bestätigen kann, example.com erreicht zu haben, und nicht, wer auch immer als Erster auf dieser IP-Adresse geantwortet hat. Fällt eine der drei weg, sind die anderen beiden kaum noch etwas wert: Ein verschlüsselter Kanal zu einem Hochstapler ist kein sicherer Kanal.
Wie der TLS-Handshake einen verschlüsselten Kanal aufbaut
Bevor überhaupt eine HTTP-Anfrage rausgeht, führen Client und Server einen Handshake aus, der sich auf einen gemeinsamen geheimen Schlüssel einigt, ohne diesen Schlüssel jemals über das Netz zu schicken. TLS 1.3, die aktuelle Version und die, die Sie einsetzen sollten, erledigt das in einem einzigen Round Trip, wo TLS 1.2 zwei brauchte.
| |
Der Client sendet ein ClientHello: die TLS-Versionen und Cipher Suites, die er unterstützt, eine zufällige Nonce, und eine Vermutung, welche Schlüsselaustausch-Gruppe der Server wählen wird, gesendet als key_share in derselben Nachricht. Diese Vermutung ist die Abkürzung von TLS 1.3. Der Server antwortet mit seinem eigenen key_share im ServerHello, und beide Seiten führen einen Diffie-Hellman-Austausch über diese Shares aus, um unabhängig voneinander denselben symmetrischen Session-Key abzuleiten. Der Schlüssel selbst überquert das Netz nie.
Ab diesem Punkt ist alles Weitere bereits unter diesem Session-Key verschlüsselt: das Zertifikat des Servers, sein Nachweis, den passenden privaten Schlüssel zu besitzen, die Finished-Nachrichten, die den gesamten Handshake per Checksumme absichern. Ein Round Trip, und die Verbindung steht.
TLS 1.2 brauchte einen zweiten Round Trip, weil Schlüsselaustausch und Cipher-Verhandlung als getrennte Schritte liefen statt sich zu überlappen. Dieser zusätzliche Trip ist reine Latenz. Auf einer Strecke mit 150ms RTT addiert sich das zu 150ms bei jeder neuen HTTPS-Verbindung, weshalb TLS 1.3 sich in den Ladezeiten der Seiten bemerkbar gemacht hat und nicht nur in der Sicherheitslage.
| TLS 1.2 | TLS 1.3 | |
|---|---|---|
| Round Trips bis zum ersten verschlüsselten Byte | 2 | 1 |
| Schlüsselaustausch | Nach der Cipher Suite verhandelt | Geraten und mit ClientHello gesendet |
| Renegotiation mitten in der Sitzung | Erlaubt | Entfernt |
| Statischer RSA-Schlüsselaustausch | Erlaubt | Entfernt (Forward Secrecy verpflichtend) |
| Schwache Cipher (RC4, CBC-Legacy) | Erlaubt | Entfernt |
Was ein Zertifikat beweist und wie die Vertrauenskette funktioniert
Der Schlüsselaustausch übernimmt die Verschlüsselung. Zu beweisen, dass der Server wirklich example.com ist, ist Aufgabe des Zertifikats. Ein Zertifikat bindet einen öffentlichen Schlüssel an einen Domainnamen und trägt die Signatur einer Certificate Authority (CA), einer Organisation, deren eigener öffentlicher Schlüssel bereits in den Trust Stores Ihres Betriebssystems und Browsers vorinstalliert ist.
In der Praxis ist die Kette drei Ebenen tief. Ein Root-CA-Zertifikat, selbstsigniert und überall vorinstalliert, signiert ein Intermediate-CA-Zertifikat. Der Intermediate signiert das Leaf-Zertifikat, das für Ihre Domain. Server präsentieren fast nie ein direkt von einer Root signiertes Zertifikat: Sie liefern das Leaf plus den Intermediate, und der Client verkettet das zurück zu einer Root, der er bereits vertraut.
Wird das Ausliefern des Intermediate vergessen, entsteht ein Fehler, der wie ein Geist wirkt. Browser mit einer gecachten Kopie dieses Intermediate verbinden sich problemlos, die Seite funktioniert also auf Ihrem Laptop; alle anderen bekommen einen Vertrauensfehler. Das ist eine der häufigsten TLS-Fehlkonfigurationen in der Produktion, und sie tritt auf der Maschine, von der aus Sie getestet haben, nie auf.
Ein Zertifikat trägt außerdem ein Gültigkeitsfenster (notBefore / notAfter) und die Hostnamen, die es abdeckt, aufgelistet in der Subject-Alternative-Name-Erweiterung (SAN). SAN ist das Feld, das Clients heute tatsächlich lesen; das alte Common-Name-Feld ist ein Relikt. Ein Zertifikat für example.com deckt api.example.com nicht ab, es sei denn, dieser Name steht ebenfalls in der SAN-Liste, oder das Zertifikat ist ein Wildcard für *.example.com.
Ein Zertifikat mit openssl s_client prüfen
Sie brauchen keinen Browser, um das alles zu sehen. openssl s_client öffnet eine rohe TLS-Verbindung und gibt aus, was ausgehandelt wurde:
| |
-servername setzt SNI (Server Name Indication), den Hostnamen, der unverschlüsselt im ClientHello gesendet wird, damit ein Server, der mehrere Domains auf einer IP hostet, weiß, welches Zertifikat er präsentieren soll. Lassen Sie es weg, hat ein Multi-Tenant-Server keine Möglichkeit zur Auswahl.
Für nur das Ablaufdatum statt des kompletten Handshake-Dumps leiten Sie in openssl x509 um:
| |
Das gibt notBefore, notAfter, das Subject (die Domain, für die das Zertifikat ausgestellt wurde) und den Issuer (die CA, die es signiert hat) aus. Für ein Monitoring-Skript, das nur ein Pass/Fail braucht, nimmt -checkend ein Zeitfenster in Sekunden und setzt den Exit-Code:
| |
curl -v ist schneller für einen schnellen Check, weil es die ausgehandelte TLS-Version und die Zertifikatskette als Teil seiner ausführlichen Handshake-Ausgabe zeigt:
| |
HTTPS auf localhost mit mkcert einrichten
Ein rohes selbstsigniertes Zertifikat, eines, das Sie selbst erzeugen und signieren statt es von einer CA zu bekommen, gibt Ihnen Verschlüsselung, aber keine Authentifizierung. Niemand bürgt dafür, also zeigt jeder Browser eine Warnung. In Ordnung für einen schnellen openssl-Test. Lästig für die lokale Entwicklung, wo https://myapp.local laden soll, ohne dass Sie die Warnung wegklicken müssen.
mkcert löst das, indem es eine lokale CA erzeugt, sie in die Trust Stores von Betriebssystem und Browser installiert und Zertifikate ausstellt, die von ihr signiert sind:
| |
Sie erhalten ein .pem-Zertifikat-Schlüssel-Paar im aktuellen Verzeichnis, dem jeder Browser auf der Maschine vertraut, weil er jetzt der von mkcert installierten CA vertraut. Richten Sie die TLS-Konfiguration Ihres Dev-Servers auf diese beiden Dateien aus, und die Warnung ist weg.
Halten Sie rootCA-key.pem aus der Versionskontrolle raus. Wer den privaten Schlüssel der lokalen CA von mkcert besitzt, kann ein Zertifikat für jede Domain ausstellen, der Ihre Maschine vertrauen wird — dieselbe Art von Risiko wie in Umgebungsvariablen und Secrets in Docker für Secrets in Umgebungsvariablen beschrieben: Ein geleakter privater Schlüssel ist ein geleaktes Credential.
Ein Produktionszertifikat mit Let’s Encrypt und certbot bekommen
In der Produktion brauchen Sie ein Zertifikat, das von einer CA signiert ist, der der Browser jedes Besuchers bereits vertraut, und Let’s Encrypt ist die kostenlose automatisierte Option, die die meisten Websites nutzen. certbot spricht das ACME-Protokoll mit den Servern von Let’s Encrypt und konfiguriert mit einem Webserver-Plugin Nginx oder Apache für Sie:
| |
Das beweist, dass Sie die Domain kontrollieren (indem ein von certbot platziertes Token ausgeliefert wird, oder über einen DNS-Eintrag, je nach Challenge-Typ), holt das Zertifikat und schreibt die Direktiven ssl_certificate und ssl_certificate_key in Ihre Nginx-Konfiguration.
Let’s-Encrypt-Zertifikate halten 90 Tage. Das kurze Fenster ist Absicht: Es macht die automatisierte Erneuerung zum einzig praktikablen Weg. Certbot installiert einen systemd-Timer, oder auf älteren Systemen einen Cron-Job, der zweimal täglich läuft und alles erneuert, was innerhalb von 30 Tagen abläuft:
| |
--deploy-hook feuert nur bei einer tatsächlichen Erneuerung, nicht bei jedem Timer-Lauf, sodass Nginx nicht zweimal täglich umsonst neu geladen wird. Testen Sie die Pipeline, bevor Sie ihr unbeaufsichtigt vertrauen:
| |
Wie TLS-Terminierung an einem Reverse Proxy oder CDN funktioniert
Die meisten Dienste terminieren TLS nie innerhalb des Anwendungsprozesses. Ein Reverse Proxy vor Ihrer App ist der übliche Ort dafür: Nginx oder Caddy hält das Zertifikat, entschlüsselt die eingehende HTTPS-Verbindung und leitet die Anfrage über einfaches HTTP im internen Netzwerk an das Backend weiter. Ihr Anwendungscode sieht nie ein Zertifikat.
| |
fullchain.pem ist das Leaf-Zertifikat mit dem bereits angehängten Intermediate, die Vertrauensketten-Datei, fertig zum Ausliefern, so wie sie ist. Nutzen Sie diese statt cert.pem, das nur das Leaf allein ist und den zuvor beschriebenen Fehler mit fehlendem Intermediate wieder einführt.
Ein CDN erledigt denselben Job einen Hop weiter draußen. Es terminiert TLS am Edge-PoP, der dem Besucher am nächsten liegt, und öffnet dann eine zweite TLS-Verbindung zurück zu Ihrem Origin. Zwei Verbindungen, zwei Handshakes, und das Zertifikat, das der Browser des Besuchers validiert, ist das Edge-Zertifikat des CDN. Ihr Origin-Zertifikat muss nur das CDN zufriedenstellen.
Diese Trennung entscheidet, wo Sie debuggen. Zeigt curl direkt gegen den Origin ein gültiges Zertifikat, die öffentliche Domain aber nicht, liegt das Problem auf der Proxy- oder CDN-Ebene, und kein noch so langes Lesen von Anwendungslogs wird das finden.
Was HSTS zusätzlich zu TLS bringt
TLS sichert eine Verbindung, sobald sie HTTPS ist. Es tut nichts für die allererste Anfrage, die noch über reines http:// rausgehen kann, wenn ein Nutzer eine nackte Domain eintippt oder einem alten Link folgt, und genau diese erste Anfrage ist der Punkt, an dem ein Angreifer auf dem Weg abfangen und auf Klartext downgraden kann. Strict-Transport-Security schließt diese Lücke: Nach einem erfolgreichen ersten HTTPS-Besuch schreibt der Browser jede spätere Anfrage an diese Domain auf HTTPS um, bevor sie die Maschine verlässt.
| |
max-age ist in Sekunden, und 63072000 sind zwei Jahre, bequem über der Ein-Jahres-Untergrenze, die für die Preload-Einreichung verlangt wird. includeSubDomains erweitert die Richtlinie auf jede Subdomain. preload markiert die Domain als geeignet für die Liste, die fest in Chrome, Firefox und Safari eingebaut ist, deren Einträge das HTTPS-Only-Rewrite bereits ab der allerersten jemals gestellten Anfrage bekommen, ganz ohne vorherigen Besuch.
Fügen Sie preload erst hinzu, wenn wirklich jede Subdomain HTTPS ausliefert. Von der Preload-Liste wieder herunterzukommen, dauert Monate, bis es sich durch die Browser-Releases zurückverbreitet hat, und bis dahin ist jede Subdomain, die kein HTTPS kann, nicht erreichbar.
Wenn TLS nicht ausreicht und was Sie zuerst prüfen sollten
TLS verifiziert den Server gegenüber dem Client. Es sagt nichts darüber aus, was dieser Server anschließend mit Ihrer Anfrage macht, und es hindert einen durch Phishing getäuschten Nutzer nicht daran, ein Passwort in eine überzeugende gefälschte Domain einzutippen, die ein völlig gültiges Zertifikat besitzt. Let’s Encrypt stellt für examp1e.com genauso bereitwillig aus wie für example.com. Ein Schloss bedeutet, dass die Verbindung privat ist, nicht, dass das andere Ende ehrlich ist.
Drei Fehler erklären die meisten Meldungen „die Seite ist down", die sich als TLS-Fehlkonfiguration entpuppen: ein abgelaufenes Zertifikat, ein fehlender Intermediate, und eine SAN-Liste, die den angefragten Hostnamen nicht abdeckt. Alle drei sind in der obigen openssl s_client-Ausgabe sichtbar, bevor Sie auch nur ein einziges Anwendungslog öffnen.
Wenn Sie TLS selbst terminieren, packen Sie diesen -checkend-Befehl noch heute in einen Cron-Job oder einen Monitoring-Alert. Das Zertifikat, das eine Seite lahmlegt, ist das, an das sich niemand mehr erinnert hat.