{"id":1623,"date":"2016-04-01T01:45:05","date_gmt":"2016-03-31T23:45:05","guid":{"rendered":"http:\/\/blog.benny-baumann.de\/?p=1623"},"modified":"2016-04-01T01:45:05","modified_gmt":"2016-03-31T23:45:05","slug":"happy-problems-with-key-pinning","status":"publish","type":"post","link":"https:\/\/blog.benny-baumann.de\/?p=1623","title":{"rendered":"Happy Problems with Key Pinning"},"content":{"rendered":"<p>Eine der diversen M\u00f6glichkeiten, sich beim Betrieb eines Webservers gnadenlos ins Knie zu schie\u00dfen, hei\u00dft HTTP Public Key Pinning oder kurz HPKP. Diese Erweiterung erlaubt es, f\u00fcr jede Domain einer Webseite exakt festzulegen, welche Public Keys in einem f\u00fcr diese Seite verwendeten Zertifikat verwendet werden d\u00fcrfen, und welche nicht. Soweit, so langweilig!<!--more--><\/p>\n<p>Denn so sch\u00f6n einfach diese Idee klingt, so viel Seil liefert einem der definierende Standard, um sich gem\u00fctlich damit zu erh\u00e4ngen. Zwar zwingt einen <a href=\"https:\/\/tools.ietf.org\/html\/rfc7469\">RFC 7469<\/a> ein Backup f\u00fcr seine Schl\u00fcssel vorzuhalten, was bei ausreichender Verpeilung und dank ungetesteter Restore-Prozeduren aber reichlich Spielraum f\u00fcr Fehler und Chaos aller Art l\u00e4sst. Vergisst man also seine Ersatz-Schl\u00fcssel, oder kann wegen Bugs in der Schl\u00fcssel-Generierung auf einen Schlag keinen seiner Ersatzschl\u00fcssel nutzen, bleibt einem lediglich der Katastrophe ihren Lauf zu lassen. Fail-Safe ist was f\u00fcr Weicheier!<\/p>\n<p>Und damit das Chaos auf dem Server auch garantiert gut dokumentiert werden kann, ist die Nutzung verschiedener Zertifizierungspfade Grundvoraussetzung. Das erschwert einem zwar die Selbstverst\u00fcmmelung beim In-Den-Fu\u00df-Schie\u00dfen, forciert aber bei der Gelegenheit, dass man schon bei der zweiten Aktualisierung versehentlich den Live-Pin aus dem Header l\u00f6scht und seine Website mit Leichtigkeit unzugreifbar macht.<\/p>\n<p>Doch, leichter geht dies mit Automatisierung, was sich bei nginx erschreckend unkompliziert konfigurieren l\u00e4sst. Schmei\u00dfen wir hierzu in die globale SSL-Konfiguration eine simple Aufforderung zum Hinzuf\u00fcgen eines Headers:<\/p>\n<pre lang=\"nginx\">\r\nadd_header Public-Key-Pins $sslcfg_cert_hpkp;\r\n<\/pre>\n<p>Die Variable $sslcfg_cert_hpkp ist hierbei der Name einer Map, die auf dem $ssl_server_name, also dem Wert der SNI-Extension basiert, und den angeforderten Hostname enth\u00e4lt. Doch wer jetzt denkt, diese Map l\u00e4sst sich in wenigen Augenblicken per Hand generieren, sollte seine LPIC-1-Zertifizierung \u00fcberpr\u00fcfen und ein kleines Script in die Konsole hacken:<\/p>\n<pre lang=\"bash\" escaped=\"true\">\r\n#!\/bin\/bash\r\n\r\nCFG=\/etc\/nginx\/conf.d\/sslcfg_cert_hpkp\r\n\r\nfunction list_domains() {\r\n    for a in \/etc\/ssl\/public\/*.crt; do\r\n        openssl x509 -in \"$a\" -text -noout -certopt no_subject,no_header,no_version,no_serial,no_signame,no_validity,no_subject,no_issuer,no_pubkey,no_sigdump,no_aux | \\\r\n        awk '\/X509v3 Subject Alternative Name:\/','\/X509v3 Basic Constraints:\/' | \\\r\n        grep -A1 \"X509v3 Subject Alternative Name:\" | \\\r\n        grep -v \"X509v3 Subject Alternative Name:\"\r\n    done | \\\r\n    tr , \"\\n\" | \\\r\n    grep DNS: | \\\r\n    cut -d: -f2 | \\\r\n    grep -v '\\*\\.' | \\\r\n    sort -u\r\n}\r\n\r\nfunction list_hpkp() {\r\n    for a in \/etc\/ssl\/public\/*.crt; do\r\n        openssl x509 -in \"$a\" -text -noout -certopt no_subject,no_header,no_version,no_serial,no_signame,no_validity,no_subject,no_issuer,no_pubkey,no_sigdump,no_aux | \\\r\n        awk '\/X509v3 Subject Alternative Name:\/','\/X509v3 Basic Constraints:\/' | \\\r\n        grep -A1 \"X509v3 Subject Alternative Name:\" | \\\r\n        grep -v \"X509v3 Subject Alternative Name:\" | \\\r\n        tr \", \" \"\\n\" | \\\r\n        grep -q \"^DNS:$1\\$\" &amp;&amp; echo \"$a\"\r\n    done | \\\r\n    sort -u\r\n}\r\n\r\nfunction gen_hpkp() {\r\n    for a in $(list_hpkp \"$1\"); do\r\n        [ $(date +%s \"--date=$(openssl x509 -in \"$a\" -noout -dates|cut -d= -f2|head -1)\") -lt $(date +%s) ] &amp;&amp; \\\r\n        [ $(date +%s \"--date=$(openssl x509 -in \"$a\" -noout -dates|cut -d= -f2|tail -1)\") -gt $(date +%s) ] &amp;&amp; \\\r\n        openssl x509 -pubkey -noout -in \"$a\" | \\\r\n        openssl rsa -pubin -inform pem -outform der | \\\r\n        openssl dgst -sha256 -binary | \\\r\n        base64\r\n    done |\r\n    xargs -i printf '; pin-sha256=\\\\\"%s\\\\\"' {}\r\n}\r\n\r\necho 'map \"$ssl_server_name\" $sslcfg_cert_hpkp {' &gt; $CFG\r\necho '    default \"\";' &gt;&gt; $CFG\r\n\r\nfor b in $(list_domains 2>\/dev\/null); do\r\n    printf \"    \\\"%s\\\" \\\"max-age=3600%s\\\";\\n\" \"$b\" \"$(gen_hpkp \"$b\" 2&gt;\/dev\/null)\"\r\ndone &gt;&gt; $CFG\r\n\r\necho '}' &gt;&gt; $CFG\r\n<\/pre>\n<p>Einmal Execute-Rechte setzen; einmal ausf\u00fchren und einmal nginx restarten. Fertig.<\/p>\n<p>Nunja, nicht ganz: Damit das funktioniert, m\u00fcssen ALLE Zertifikate unter \/etc\/ssl\/public liegen &#8211; inklusive derer, die aktuell nicht verwendet werden, sondern als Backup dienen. Aber das sollte f\u00fcr einen ge\u00fcbten Admin ja machbar sein.<\/p>\n<p>P.S.: Jede Domain muss zu jedem Zeitpunkt mindestens 2 g\u00fcltige Zertifikate haben. Mehr schaden in der Regel nicht &#8230;<\/p>\n<p class=\"wp-flattr-button\"><a href=\"https:\/\/blog.benny-baumann.de\/?flattrss_redirect&amp;id=1623&amp;md5=e70e21d61f237c3628f09908886ead25\" title=\"Flattr\" target=\"_blank\"><img src=\"http:\/\/blog.benny-baumann.de\/wp-content\/plugins\/flattr\/img\/flattr-badge-large.png\" srcset=\"http:\/\/blog.benny-baumann.de\/wp-content\/plugins\/flattr\/img\/flattr-badge-large.png\" alt=\"Flattr this!\"\/><\/a><\/p>","protected":false},"excerpt":{"rendered":"<p>Eine der diversen M\u00f6glichkeiten, sich beim Betrieb eines Webservers gnadenlos ins Knie zu schie\u00dfen, hei\u00dft HTTP Public Key Pinning oder kurz HPKP. Diese Erweiterung erlaubt es, f\u00fcr jede Domain einer Webseite exakt festzulegen, welche Public Keys in einem f\u00fcr diese Seite verwendeten Zertifikat verwendet werden d\u00fcrfen, und welche nicht. Soweit, so langweilig!<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"ngg_post_thumbnail":0,"footnotes":""},"categories":[4],"tags":[],"class_list":["post-1623","post","type-post","status-publish","format-standard","hentry","category-server"],"_links":{"self":[{"href":"https:\/\/blog.benny-baumann.de\/index.php?rest_route=\/wp\/v2\/posts\/1623","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.benny-baumann.de\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.benny-baumann.de\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.benny-baumann.de\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.benny-baumann.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1623"}],"version-history":[{"count":2,"href":"https:\/\/blog.benny-baumann.de\/index.php?rest_route=\/wp\/v2\/posts\/1623\/revisions"}],"predecessor-version":[{"id":1634,"href":"https:\/\/blog.benny-baumann.de\/index.php?rest_route=\/wp\/v2\/posts\/1623\/revisions\/1634"}],"wp:attachment":[{"href":"https:\/\/blog.benny-baumann.de\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1623"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.benny-baumann.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1623"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.benny-baumann.de\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1623"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}