HTTP Public Key Pinning / HPKP -> Erklärung und Einrichtung

Ein Problem „by-Design“ ist, dass jede CA für jeden Webserver Zertifikate ausstellen kann. Kaufe ich mir bei der CA foo für die Domain www.example.org ein Zertifikat, bin ich nicht davor geschützt, dass die CA bar kein Zertifikat für die gleiche Domain ausstellt. Alles schon vorgekommen… Da Webbrowser von Haus aus hunderten Zertifizierungsstellen vertrauen ist das auch kein Problem theoretischer Natur mehr, und mit Superfish oder Privdog sogar brandaktuell.

Es gibt keine 100%tige Lösung für das Problem mit den CAs, aber eine, die einen deutlichen Gewinn an Sicherheit bringt, und das für jede HTTPS Verbindung. Das ganze heißt HTTP Public Key Pinning und ist einfach erklärt: Der Webserver sendet bei seiner Antwort in einem HTTP Header mindestens zwei Informationen: a) Pins von zwei Schlüsseln und b) die Information wie lange diese gültig sein sollen. Der Webbrowser merkt sich diese Informationen und verweigert die Kontaktaufname, wenn die Pin des gesendeten Zertifikates nicht mit einer Pin aus dem HTTP Header übereinstimmt. Eine Pin ist übrigens der base64 encodete SHA256-Hash Fingerprint eines public Keys eines Zertifikats [Update: Danke Puhh für die Korrektur].
Die empfohlene Gültigkeit der Informationen liegt bei 60 Tagen, Das erklärt auch warum es zwei Pins sein müssen. Verliert man nämlich einen Key, oder dieser wird wegen einer Sicherheitslücke unsicher (Gruß an Heartbleed), schließt man potentielle Leser im schlimmsten Fall 60 Tage lang von seiner Webseite aus. Um hier das Risiko zu verringern müssen immer zwei Pins angegeben werden wovon eine die eines Backup Keys ist.

In der Praxis sieht das wie folgt aus:

  • Zwei Keys generieren:
    openssl genrsa -out www.example.org.hpkp1.key 4096
    openssl genrsa -out www.example.org.hpkp2.key 4096
  • Mit dem ersten Key einen CSR erstellen:
    openssl req -new -sha256 -key www.example.org.hpkp1.key -out www.example.org.csr
  • Zertifikat mit dem CSR besorgen
  • Zertifikat im Apache vhost einbinden
  • HPKP Header generieren. Dafür kann zum Beispiel das Skript hpkp-gen genommen werden.
  • HPKP Header in Apache einrichten. Dafür muss zuerst das Modul Headers aktiviert sein:
    a2enmod headers

    und danach der generierte HPKP Header in den vhost eingetragen werden:

      ## Header rules
      ## as per http://httpd.apache.org/docs/2.2/mod/mod_headers.html#header
      Header always set Public-Key-Pins: 'max-age=5184000; pin-sha256="+sCGKoPvhK0bw4OcPAnWL7QYsM5wMe/mn1t8VYqY9mM="; pin-sha256="bumevWtKeyHRNs7ZXbyqVVVcbifEL8iDjAzPyQ60tBE="'
  • Monitoring anpassen. Dieses sollte meckern sobald das Zertifikat in weniger als 60 Tagen abläuft. Dann sollte man mit dem Backup Key einen neuen CSR erzeugen, ein neues Zertifikat hinterlegen, einen neuen Backup-Key erstellen und auch den HPKP Header in Apache anpassen und die Pins entsprechend durchrotieren.
  • SSLLabs Test machen. Dort sollte ganz unten im letzten Kasten und darin im vorletzten Abschnitt „Public Key Pinning (HPKP)“ in grün und mit yes stehen. HSTS+HPKP

HPKP bringt darüber hinaus noch etwas spannendes mit. Wenn der Webbrowser eine Verbindung ablehnt, weil der Pin des vom Webserver gesendeten Zertifikates nicht mit einer im HTTP Header angegebenen oder im Webbrowser gespeicherten Pin übereinstimmt, kann dieser eine bestimmte Stelle informieren. Diese Stelle kann im HPKP Header optional mit angegeben werden. Dafür wird dort noch eine report-uri=“http://www.example.org/hpkpReportUrl“ hinzugefügt. Der Header sieht dann zum Beispiel so aus:

Public-Key-Pins: 'max-age=5184000; pin-sha256="+sCGKoPvhK0bw4OcPAnWL7QYsM5wMe/mn1t8VYqY9mM="; pin-sha256="bumevWtKeyHRNs7ZXbyqVVVcbifEL8iDjAzPyQ60tBE="; report-uri="http://www.pregos.info/hpkp.php"'

Die Info wird im JSON Format übertragen und als POST gesendet.

Update
Hier das hpkp.php Skript:

<?php
 
/* script that can be used as a report-uri for HPKP.
   Will just send a mail to $hpkp_to with some info and json data
   Written by Hanno Böck, https://hboeck.de/
   License: CC0 / Public Domain
*/
 
$hpkp_to = "[insert email]";
$hpkp_log = "../hpkp.log";
 
$hpkp_info = "Host: ".$_SERVER['HTTP_HOST']."n";
$hpkp_info .= "Request URI: ".$_SERVER['REQUEST_URI']."n";
if ( array_key_exists('HTTP_REFERER', $_SERVER) ) {
$hpkp_info .= "Referrer: ".$_SERVER['HTTP_REFERER']."n";
}
$hpkp_info .= "Remote IP: ".$_SERVER['REMOTE_ADDR']."n";
$hpkp_info .= "User agent: ".$_SERVER['HTTP_USER_AGENT']."n";
$hpkp_info .= "CSP JSON POST data:nn";
$hpkp_info .= str_replace( ",", ",n", file_get_contents('php://input') );
$hpkp_info .= "nServer Info:nn";
$hpkp_info .= print_r($_SERVER, true);
 
 
mail($hpkp_to, "HPKP Warning from ".$_SERVER['HTTP_HOST'], $hpkp_info);
echo "ok";
 
if ($hpkp_log != "") {
	$f = fopen($hpkp_log, "a");
	fwrite($f, $hpkp_info);
	fclose($f);
}

Mehr Sicherheit fuer die eigenen Daten – sichere Zugangsdaten / Passwoerter

Dieses ist der fuenfte Teil einer Serie von Blogeintraegen, die sich als Reaktion auf die NSA Affaere um den Kontext Sicherheit fuer die eigenen Daten und Verschluesselung drehen.

Links zu den ersten vier Artikeln befinden sich am Ende des Blogposts. Im diesem fuenften Teil schreibe ich etwas zu meinen Ueberlegungen zu sicheren Zugangsdaten und Passwoertern.

Ich gebe zu: Ich war faul! Ja, ich hatte Standardpasswoerter unterschiedlicher Staerke und unterschiedlichen Typs, die ich an verschiedenen Stellen wiederholt habe. Ich weiss, es ist boese, ich habe es aber trotzdem getan.

Wenn man ueber Einbrueche bei Diensten im Internet und die damit verbundene Aufforderung doch bitte sein Passwort zu aendern liesst, dann wird meistens gesagt, das wenn man die Kombination aus Benutzername und Passwort noch woanders genutzt hat, das man ueberall dort zur Sicherheit das Passwort aendern muss. Daraus ist relativ einfach zu schliessen, dass man die Kombination aus Benutzernamen und Passwort nie zweimal verwenden sollte. Der Haken an der Sache ist aber, das man sich die ganzen Passwoerter ja auch merken koennen muss. Aus diesem Grund kann man entweder ein Passwortschema benutzen, oder man generiert wirklich konsequent Zufallspasswoerter. Diese Passwoerter kann mann dann zum Beispiel im Webbrowser speichern oder man notiert sie sich in einer Datei in einem verschluesselten Container.

Ich habe mich fuer eine Kombination aus verschiedenen Modellen entschieden, die fuer mich am praktischsten ist. Als erstes verwende ich bei Diensten als Benutzername nach Moeglichkeit eine Emailadresse. Da ich alle meine Emails ueber einen eigenen Mailserver versende lege ich mir fuer jeden neuen Account einen neuen Alias an. Das ist zwar zu Anfang nervig, hat aber den witzigen Nebeneffekt, das man sofort mitbekommt wo die eigene Emailadresse unautorisiert weitergegeben wurde ;-). Der Zugangsname ist also in der Regel unterschiedlich.

Bei den Passwoertern habe ich lange ueberlegt wie ich es mache, und habe mich am Ende fuer einen webbasierten Passwortgenerator entschieden, den ich bei mir auf meinem eigenen Server installieren kann. Dort wird aus der Kombination von Benutzername, Domain und eigenem Passwortstring ein bcrypt Hash gebildet, den ich dann als Passwort nutze. Weitere Informationen zu dem Tool gibt es hier:

Leider sind die bcrypt Hashes in der freien Wildbahn manchmal schon zu sicher, da sie Zeichen enthalten, die bestimmte Dienste nicht erlauben. Dies liegt (meistens) an der verwendeten Programmiersprache. In WordPress ist zum Beispiel kein Backslash erlaubt. Diese unerlaubten Zeichen filtere ich von Hand aus dem generierten Hash heraus. Da ich die Passwoerter ja aber sicher abgespeichert habe muss ich sie nicht oft generieren. Wenn ja, muss ich fuer zusaetzliche Sicherheit damit leben, dass ich mir manche Passwoerter erst zusammenbasteln muss.

Am Ende habe ich aber wieder zwei Arten von Passwoertern. Einmal die bcrypt Hashes, die ich nach Moeglichkeit ueberall verwende. Aber mal ehrlich, wer moechte sich Passwoerter wie:

  • B1-r6UF9s:Mb<% (test, test, test im Generator)

schon gerne merken um es bei Firefox als Masterpasswort oder beim verschluesselten Container als decrypt Phrase einzugeben? Ich nicht! Aus diesem Grund existiert auch weiterhin ein einfach zu merkendes, aber dennoch nach den Regeln der Kunst sicheres Passwort in meinem Kopf, das je nach genutztem Dienst noch etwas hinten dran bekommt.

Durch die Kombination aus moeglichst jeweils unterschiedlichem Loginnamen, bcrypt Hashes als Passwoertern und nur ganz minimal gebrauchtem sicheren Passwort in meinem Kopf habe ich einiges an Sicherheit gewonnen. Nach und nach wechsle ich nun ueberall meine Passwoerter auf dieses neues System.

 

Vorherige Blogposts:

  • Der erste Teil war fuer mich das Aufraeumen, einen Ueberblick zu bekommen sowie Strukturen zu schaffen, auf denen ich aufbauen kann.
  • Der zweite Teil bestand darin einen Ort zu schaffen, in dem ich Keys und Passwoerter sicher aufbewahren und gleichzeitig alles in ein vernuenftiges Backup schieben kann.
  • Der dritte Teil bezog sich auf das erzeugen von Zertifikaten und Einrichten von verschluesselten Verbindungen zu Apache vHosts.
  • Der vierte Teil drehte sich um das Thema Komfort im Webbrowser und verwies in dem Kontext auf einen Artikel zum selbst gehosteten Firefox Sync Server.