Let's Encrypt & Public Key Pinning in der Praxis einsetzen

Let’s Encrypt verbreitet sich schnell und mittlerweile bieten viele Hosting-Provider die freie TLS-Verschlüsselung ihren Kunden an. Über fünf Millionen Domainzertifikate hat die Zertifizierungsstelle (CA) bislang ausgestellt, Tendenz weiter steigend. So habe ich inzwischen auch eine Testphase gestartet. In den letzten Tagen habe ich mich eingehender mit HTTP Public Key Pinning (HPKP) im Zusammenhang mit Let’s Encrypt beschäftigt. Seitdem ich HPKP einsetze, habe ich bisher ausschließlich das Domainzertifikat gepinnt.

Let's Encrypt: kostenlose TLS-Zertifikate auf Knopfdruck
Let's Encrypt: kostenlose TLS-Zertifikate auf Knopfdruck

Durch die kurzen Gültigkeitszeiträume eines Let’s-Encrypt-Zertifikates ist das allerdings problematisch. Daher musste ich mir Gedanken machen, wie sich Public Key Pinning in der Praxis gemeinsam mit Let’s Encrypt am besten verträgt. Bei meinen Experimenten sind mir einige Feinheiten von HPKP bewusster geworden. Sodass ich mir die Frage stellte, was mir wichtiger ist: Zertifikate mit kurzer Gültigkeit oder das Pinning selbst?

Wirkungsweise von Public Key Pinning kurz erklärt

Der Einsatz von HPKP wird wahrscheinlich oftmals missverstanden. Mir ging es da nicht anders. Diese eine kleine Zeile in der Serverkonfiguration kann ungeheuer machtvoll und falsch eingesetzt total nutzlos sein. Der Aufbau ist schnell erklärt. Es braucht mindestens zwei SPKI-Pins — einen gültigen Pin aus der aktuellen Zertifikatskette und einen Backup-Pin, für später oder den Ernstfall. Zudem ist die Angabe der Cachedauer von großer Bedeutung.

Header always set Public-Key-Pins "pin-sha256=\"base64+primary==\"; pin-sha256=\"base64+backup==\"; max-age=5184000;"

Der HTTP-Header wird vom Server beim ersten Aufruf der Webseite an den Browser gesendet und für die angegebene Cachezeit gespeichert. Fortan prüft der Client bei jedem Besuch während dieser Zeitspanne, ob mindestens einer der hinterlegten Pins mit einem der Zertfikate übereinstimmt. Trifft dies gar nicht zu, wird die Verbindung unterbrochen.

HPKP schafft nicht die erhoffte Sicherheit

Das klingt alles schön und sicher, lässt sich in der Praxis aber leider schnell aushebeln. Die Informationen sind nur bis zum Ende der Cachedauer gültig und werden danach erneut abgerufen. Beim ersten Aufruf wird jedoch die Richtigkeit der Daten nicht geprüft. Bedeutet, der Header kann ungültige Pins enthalten und die Webseite wird trotzdem geladen. Nur wenn mindestens ein Pin stimmt, ist das Pinning quasi auch aktiv. Ansonsten ignoriert der Client den Header.

Wer regelmäßig seinen Browsercache löscht, sorgt so dafür, dass der Server beim erneuten Seitenbesuch den Header abermals sendet und dieser somit neu gespeichert wird. So ist der Sicherheitszuwachs durch HPKP dummerweise gehemmt. Würde bereits beim ersten Aufruf ein Abgleich erfolgen, wäre dies deutlich sicherer. Dann spielt die Cachedauer keinerlei Rolle mehr.

Auf netcraft.com hat Paul Mutton weitere Beispiele für eine fehlerhafte HPKP-Implementierung aufgeführt. Scott Helme bietet dagegen in seinem Blog einen umfangreichen Guide zu HPKP. Beide Beiträge sind sehr gut, um die Wirkungsweise von HPKP besser verstehen zu lernen.

Best Practice: Let’s Encrypt + HTTP Public Key Pinning

Es gilt, den Einsatz von HPKP im Zusammenspiel mit Let’s Encrypt zu überdenken. Wenn man bedenkt, dass das Widerrufen von Zertifikaten in der Praxis bisweilen mehr schlecht als recht funktioniert und auch HTTP Public-Key-Pins nicht die erhoffte Sicherheit bewirkt, sind möglichst kurze Gültigkeitszeiträume für TLS-Zertifikate der absolute Sieger. Let’s Encrypt plant auch irgendwann, die Gültigkeit von bisher 90 Tagen weiter herabzusetzen. Basierend auf diesem Wissen habe ich ein mögliches Best Practice entworfen:

Header set Public-Key-Pins
# Let's Encrypt DST Root CA X3 - Haupt CA
"pin-sha256=\"Vjs8r4z+80wjNcr1YKepWQboSIRi63WsWXhIMN+eWys=\";
# Let's Encrypt Authority X3
pin-sha256=\"YLh1dUR9y6Kja30RrAn7JKnbQG/uEtLMkBgFF2Fuihg=\";
# Let's Encrypt Authority X4
pin-sha256=\"sRHdihwgkaib1P1gxX8HFszlD+7/gTfNvuAybgLPNis=\";
# StarCom CA - Backup CA
pin-sha256=\"5C8kvU039KouVrl52D0eZSGf4Onjo4Khs8tmyTlV3nU=\";
# StarCom Class 1 DV Server CA
pin-sha256=\"Fbs+o+IxVNTHBpjNQYfX/TBnxPC+OWLYxQLEtqkrAfM=\";
# Backup Pin 1
pin-sha256=\"NooJ9hiKyRsSGqA/koXPlY4DEZ77iLZx60XRuGaikZA=\";
# Backup Pin 2
pin-sha256=\"pPWeyn3jMRmWxagGkmzFM62rVgZKRaCuwB2YJWM+ukU=\";
# Gültigkeit: 2 Monate
max-age=5184000"

Was bewirkt mein Ansatz?

Zuerst habe ich mich dazu entschieden, vom Root aus zu pinnen. Demnach wäre es einem Betrüger nur möglich, von den von mir genutzten CAs Zertifikate auf meine Domain ausstellen zu lassen. Da Let’s Encrypt ausgestellte Zertifikate an das Certificate Transparency Log meldet, würde das rasch auffallen. Auch ein Root-Zertifikat kann mal ablaufen oder muss erneuert werden. Der Zugriff auf meinen Blog soll jedoch möglichst offen sein.

Deshalb habe ich zusätzlich zwei Zwischenzertifikate von Let’s Encrypt hinterlegt und zugleich noch eine zweite Zertifizierungsstelle, inklusive Zwischenzertifikat, ins Spiel gebracht. In meinem Fall StartCom, da mein aktuelles Zertifikat noch darüber läuft und ich im Notfall eine Ausweich-CA nutzen kann. Für ein neues Zertifikat existieren dann noch zwei Backup-Pins. Die Cachedauer beträgt zwei Monate.

HPKP ist nur ein Sicherheitszusatz!

Mit dieser Konfiguration fahre ich in meinen Augen am besten, verbaue keine Wege und habe trotzdem noch einen eventuellen Sicherheitszuwachs. Im Endeffekt ist HPKP für mich eine zusätzliche Schutzfunktion, jedoch komplett ohne Garantie. Ob HPKP wirkt oder nicht, hängt von den weiter oben beschriebenen Faktoren ab. Daher sind Zertifikate mit einer kurzen Gültigkeit die sicherste Bank. Vor allem wichtig sind die immer neuen Private Keys. Dann spielt es eine geringere Rolle, ob ein Angreifer diesen abgreifen kann.

Abgelaufen ist abgelaufen und damit automatisch ungültig!

Schreib mir gern deine Meinung, wenn du Vorschläge oder Anregungen zu dem Thema hast.

Weitere interessante Themen zu HTTPS


Avatar von reraiseace
Autor: Markus Werner (reraiseace) Twitterreraiseace, Google+reraiseace, Twittercb_werner
Ich bin Redaktionsvolontär bei der COMPUTER BILD in Hamburg, Fernstudent am Deutschen Journalistenkolleg und schreibe auf re{raise}ace privat über Webdesign und Programmierung. Seit 2015 schrieb ich auch regelmäßig für andere Medien.

7 Kommentare – Schreib mir deine Meinung!

Fülle bitte die nachstehenden Felder aus. Angaben mit einem Sternchen sind Pflichtangaben. Deine E-Mail-Adresse wird nicht veröffentlicht.

  1. Kommentar von Peter · · #

    Hallo Markus,

    auf welche Weise hast du die letsencrypt-Zertifikate erstellt? Mit den automatisierten Letsencrypt-Tools habe ich doch keinen Einfluss auf den CSR und damit den PIN, oder?

    Gruß

    Peter

  2. Kommentar von reraiseace · · #

    Die Zertifikate werden bei mir direkt vom System mit dem LE-Tool erzeugt. Richtig, auf den CSR und damit auch den PIN hast du keinen Einfluss. Es kommt drauf an, wie du HPKP einsetzen willst. Bei LE wird aktuell in der Regel alle 60 Tage ein neues Zertifikat ausgestellt, dass maximal nach 90 Tagen getauscht werden sollte. Das sind recht kurze Zeiträume. Man kann das Domainzertifikat pinnen, was ich aber nicht empfehlen würde, wie weiter oben geschrieben. Daher habe ich mich dazu entschieden von der Rootebene aus zu pinnen und da spielt dann das Domainzertifikat keine Rolle. Durch meinen Ansatz werden nur Zerifikate von LE und der Backup CA akzeptiert. Die Kontrolle/Übersicht behält man schlussendlich auch dank dem Certificate Transparency Log.

  3. Kommentar von Martin · · #

    Vielen Dank für den Artikel. Welche keys verwendest du denn konkret als „Backup PIN 1 und 2“ und was macht sie konkret zu einem guten Backup?

    Nehmen wir einmal an keine der gepinnten CA (LE X3/X4, StarCom) kann dir nächsten Monat ein brauchbares Zertifikat ausstellen. Sei es weil du darauf keinen Einfluss hast (vielleicht stellt LE bald über eine andere Root/Intermediate aus) oder die CA aus den Truststores fliegt (siehe StarCom).

    In dem Fall würden Browser für ~2 Monate keine fehlerfreie Verbindung mehr aufbauen da keine der PINs in deinem HPKP header für den host gültig (trustchain) oder vertrauenswürdig (truststore) wäre. Das gleiche trifft natürlich zu wenn die certs deiner Backup PINs self-signed sind.

    Ich gebe zu der Fall ist unwahrscheinlich, hätte aber fatale Folgen wenn man HPKP verwendet. Einige andere Beispiele empfehlen gar max-age Werte von 2 Jahren – sollte der Fall bei einer solchen Konfiguration eintreten, könnte man seine Domain eigentlich gleich wegwerfen.

  4. Kommentar von reraiseace · · #

    Gern geschehen :-) Als Backup PINs verwende ich zwei selbst erzeugte und ungenutzte CSRs, mit denen ich im Notfall zu jeder x-beliebigen Zertifizierungsstelle gehen kann. So erhalte ich jederzeit ein gültiges Zertifikat und gleichbedeutend einen gültigen PIN für HPKP, da mindestens ein PIN stimmen muss.

    Wenn der von dir beschrieben Fall eintritt, verwende ich also einen der beiden Backup PINs und gehe damit zu irgendeiner CA. Theoretisch ist die Angabe einer Backup CA nicht zwangsweise notwendig. Ich habe mich nur dafür entschieden, da auch andere Zertifizierungsstellen in kürzeren Intervallen neue Zertifikate erzeugen, nur komplett neue Zertifikate ausstellen oder ich mein Backup in Ruhe lassen kann. Seit dem Skandal mit WoSign setze ich nicht mehr auf StartCom, das aber nur am Rande.

    Ja, der Fall ist relativ unwahrscheinlich, aber man sollte ihn gedanklich mal durchspielen. Angenommen, du hättest keinen CSR in der Hinterhand und die Cache-Dauer beträgt 2 Monate oder länger, dann hätte das nur bedingt fatale Folgen. Warum? Die Informationen von HPKP werden beim ersten Besuch im Browser-Cache abgelegt, nicht geprüft und dann für die angegeben Dauer gespeichert. Danach geht das Spielchen wieder von vorne los. Du brauchst nur den Browser-Cache löschen und erhältst sofort wieder Zugriff, solltest den Header dann aber korrigieren. Demnach müsstest du deinen Seitenbesuchern nur mitteilen, dass sie bitte alle mal ihren Browser-Cache leeren sollten. Die fehlende Überprüfung beim ersten Aufruf ist in meinen Augen ein Designfehler.

  5. Kommentar von Martin · · #

    Danke für die Antwort, das macht es um einiges klarer. Vielleicht hilft es anderen: den Hash eines CSR kann man wie folgt bilden: „openssl req -pubkey -in backup.csr | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | base64“

    Bezüglich des Risikos kein Backup zu haben oder eine fehlerhafte Konfiguration live zu nehmen denke ich schon dass es viele Bereiche gibt in denen es fatal ist wenn Nutzer auch nur einige Tage keine (erneute) Verbindung aufbauen können, zum Beispiel Online-Shops. Geschätzt five-nine der Nutzer dürften mit der HPKP Fehlermeldung ihres Browsers nichts anfangen können und da erst keine Verbindung zustande kommt hat man auch keine Möglichkeit dem Nutzer einen workaround zu beschreiben.

    Die Überprüfung kann nicht beim ersten Aufruf passieren da die HPKP header, wie der Name schon sagt, als HTTP header geschickt werden. Der Aufbau einer gesicherten Verbindung (TLS) und übertragen der Public-Keys passiert bevor der Browser zum ersten mal HTTP spricht und die Information über pinning erhält, es sind also unterschiedliche Protokollebenen. Ich gebe dir Recht dass es besser wäre die gepinnten keys vorher zu prüfen, dafür müsste man sie aber außerhalb von HTTP transportieren, zum Beispiel als DNS Einträge wie das bei DNSKEY resource records passiert.

  6. Kommentar von reraiseace · · #

    Gern geschehen und danke für den Kommandobefehl um einen CSR via OpenSSL zu erzeugen.

    Du hast schon recht, es kommt am Ende auf den Bereich an. Im Grunde schreibt HPKP auch mindestens einen Backup PIN vor. Damit sollte sowas, wenn korrekt umgesetzt, nicht bzw. nur sehr selten passieren.

    Mit der Überprüfung hast du genauso Recht, das ist mir auch bewusst. Dein Ansatz wäre eine mögliche Idee.

  7. Kommentar von Martin · · #

    Danke für diesen hilfreichen Artikel, hatte HPKP auch schon länger auf meiner ToDo-Liste und habe es mit deiner Hilfe jetzt endlich umgesetzt: https://blog.mdosch.de/2017/02/09/http-public-key-pinning/