HTTP Public Key Pinning mit Let’s Encrypt Zertifikaten

Achtung! Dieser Artikel ist älter als ein Jahr. Der Inhalt ist möglicherweise nicht mehr aktuell!

Es war mal wieder soweit. Drei Monate sind rum und mein Monitoring vermeldete: „You Certificate expires in 15 days“. Die Erneuerung war dank Let’s Encrypt kein Problem und innerhalb einer Minute durch. Den HTTP Header für HTTP Public Key Pinning anpassen war ein Kampf, da ich keine Dokumentation drüber hatte. Das hole ich jetzt nach.

Hashes erstellen

Für den Header braucht man die Base64 encoded SHA-256 Hashes der Zertifikate. Im HTTP Header müssen mindestens zwei Pins angeben und mindestens ein Pin darf nicht einem Zertifikat in der Chain matchen.

Andere Tutorials erstellen einen „Backup“ Certificate Signing Request. Da das ganze CSR erstellen und signieren lassen der Job vom Let’s Encrypt Client ist, gehe ich einen anderen Weg: Ich denke mir den dritten Pin einfach aus. Ganz sauber ist das nicht, jedoch geht es ohne zwei Pins nicht und es ist besser als gar kein Pinning.

Pin 1 wird vom Zertifikat an sich generiert, der benötigte zweite valide Pin wird vom Let’s Encrypt CA Zertifikat erstellt.

Der Let’s Encrypt Client speichert die Zertifikate und alle anderen benötigten Datein unter /etc/letsencrypt/live/>domain</. Bei mir also unter /etc/letsencrypt/live/blog.veloc1ty.de/.

In diesem Verzeichnis habe ich folgende Dateien:

root@mineralwasser:/etc/letsencrypt/live/blog.veloc1ty.de# ls -1
cert.pem
chain.pem
fullchain.pem
privkey.pem

In der Datei cert.pem ist das ausgestellte Zertifikat der Let’s Encrypt CA. In der Datei chain.pem ist das Zertifikat der Let’s Encrypt CA. Von diesen beiden Dateien erzeugt man nun die Strings:

:~$ openssl x509 -pubkey < cert.pem | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | base64
SldPpjUUcpfYLm1l8x3btmjepByNg5uLCrmk7yLi4gw=
:~$ openssl x509 -pubkey < chain.pem | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | base64
YLh1dUR9y6Kja30RrAn7JKnbQG/uEtLMkBgFF2Fuihg=

Und dann denkt man sich noch einen Fantasie String aus:

BLh1dUR9y6Kja30RrAn7JKnbQG/uEtLMkBgFF2Fuihg=

Konfiguration von nginx

Diese beiden Hashes müssen nun in den dazu gehörigen HTTP Header geschrieben werden. In der vhost Datei für meinen Blog habe ich im server-Block folgende Zeile:

add_header "Public-Key-Pins" "pin-sha256=\"SldPpjUUcpfYLm1l8x3btmjepByNg5uLCrmk7yLi4gw=\"; pin-sha256=\"YLh1dUR9y6Kja30RrAn7JKnbQG/uEtLMkBgFF2Fuihg=\"; pin-sha256=\"BLh1dUR9y6Kja30RrAn7JKnbQG/uEtLMkBgFF2Fuihg=\"; max-age=1296000";

Wichtig: Die Anführungszeichen richtig Escapen!

Max-Age gibt an, wie lange die Pins gültig sind. Da Let’s Encrypt keine lang läufigen Zertifikate ausstellt muss der Wert hier niedrig sein. Die Angabe ist in Sekunden. Ich habe hier 15 Tage angegeben.

Nach dem Reload von nginx wird der HTTP Header immer eingetragen und die Browser speichern sich die Pins. Beim Erneuern muss man nur noch den ersten Pin anpassen, da der zweite fix ist.

Testen

Es gibt ein guten online Tool für das Testen. Dieses Tool findet man hier: https://report-uri.io/home/pkp_analyse/

public_key_pins

Further Reading


Du hast einen Kommentar, einen Wunsch oder eine Verbeserung? Schreib mir doch eine E-Mail! Die Infos dazu stehen hier.

🖇️ = Link zu anderer Webseite
🔐 = Webseite nutzt HTTPS (verschlüsselter Transportweg)
Zurück