- HSTS preloading: browser already knows if a site has to be contacted via HTTPS only. For instance, it ensures users go to https://site even if they type http://site. This protects against MITM attacks when reaching http://site for the first time (see how sslstrip strips the "s" of https).
- public key pinning: certificate chain must include a whitelisted public key. For instance, it ensures only whitelisted Certificate Authorities (CA) can sign certificates for *.example.com, and not any CA in your browser store.
Enable HSTS
You can enable HSTS in Apache with mod headers and a line in your configuration:<IfModule mod_headers.c> # this domain should only be contacted in HTTPS for the next 6 months Header add Strict-Transport-Security "max-age=15768000" </IfModule>Note: you can append "; includeSubDomains" if you want HSTS to inherit to subdomains (I do not, because this blog is at blogger). Restart Apache and check if it works:
$ curl -si https://stalkr.net | grep ^Strict Strict-Transport-Security: max-age=15768000Then you probably want to redirect all HTTP traffic to HTTPS with mod rewrite:
<IfModule mod_rewrite.c> RewriteEngine On RewriteCond %{HTTPS} off RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} </IfModule>Reference: Protecting your users from phishing with Apache rules and HSTS.
Chrome's HSTS interface
Using HSTS tab in Chrome version 13 and above, you can fully enjoy HSTS preloading and public key pinning.With "query a domain", you can ask for a domain and it gives you its status in Chrome:
- found: is this domain in local database?
- mode: refers to HSTS mode of operation. STRICT enforces HTTPS connections while NONE allows HTTP connections. As far as I know, there are no other modes.
- include_subdomains: do mode and pubkey_hashes propagate to subdomains?
- domain: simply the domain considered.
- is_preloaded: is this domain HSTS-preloaded in Chrome? For now it is hardcoded in the binary and will hopefully grow. You can contact Chromium to have your site included in that list.
- pubkey_hashes: refers to public key pinning, it is the list of <hash algorithm>/<base64-encoded public key hash>
As an example, querying mail.google.com gives:
Found: mode: STRICT include_subdomains: true domain: mail.google.com is_preloaded: true pubkey_hashes: sha1/4n972HfV354KP560yw4uqe/baXc=, sha1/IvGeLsbqzPxdI0b0wuj2xVTdXgc=, sha1/QMVAHW+MuvCLAO3vse6H0AWzuc0=, sha1/AbkhxY0L343gKf+cki7NVWp+ozk=, sha1/SOZo+SvSspXXR9gjIBBPM5iQn9Q=
Browsing to https://stalkr.net, Chrome receives the HSTS header and automatically adds the domain to the HSTS set, as we can see by querying stalkr.net:
Found: mode: STRICT include_subdomains: false domain: stalkr.net is_preloaded: false pubkey_hashes: (none)is_preloaded: true confirms that the domain was not HSTS preloaded in Chrome but has been added by browsing.
Public key hashes
As well explained by Adam Langley (@agl__) in his post, public keys are hashed instead of certificates (more often reiussed). The hash is not the one of the public key bit string but the one of the SubjectPublicKeyInfo (SPKI) which can be obtained with openssl -pubkey.Python script to create/view HTTP pins: http_pins.py.
So what is exactly the first hash in pubkey_hashes for mail.google.com?
$ python http_pins.py 4n972HfV354KP560yw4uqe/baXc= SPKI fingerprint (sha1): e2:7f:7b:d8:77:d5:df:9e:0a:3f:9e:b4:cb:0e:2e:a9:ef:db:69:77
It looks like Verisign Class 3 Public Primary CA (cert), found in ca-certificates package:
$ python http_pins.py /etc/ssl/certs/Verisign_Class_3_Public_Primary_Certification_Authority.pem /etc/ssl/certs/Verisign_Class_3_Public_Primary_Certification_Authority.pem: * SPKI fingerprint (sha1): 65:e7:46:e3:e3:73:e3:a0:df:5f:5b:e8:8e:e2:73:e1:f2:fc:f7:92 * SPKI fingerprint (sha256): 08:56:e6:57:db:ef:1e:1a:bc:a4:b3:e8:75:0c:21:d3:c0:96:ba:b2:6b:d8:55:cb:9b:f4:86:76:5d:41:85:80 Public-Key-Pins: max-age=600; pin-sha1=ZedG4+Nz46DfX1vojuJz4fL895I=; pin-sha256=CFbmV9vvHhq8pLPodQwh08CWurJr2FXLm/SGdl1BhYA=Confirmed by Chromium source code (search kGoogleAcceptableCerts).
What about the others?
$ python http_pins.py IvGeLsbqzPxdI0b0wuj2xVTdXgc= SPKI fingerprint (sha1): 22:f1:9e:2e:c6:ea:cc:fc:5d:23:46:f4:c2:e8:f6:c5:54:dd:5e:07VeriSign Class 3 Public Primary Certification Authority - G3 (cert), found in ca-certificates package.
$ python http_pins.py QMVAHW+MuvCLAO3vse6H0AWzuc0= SPKI fingerprint (sha1): 40:c5:40:1d:6f:8c:ba:f0:8b:00:ed:ef:b1:ee:87:d0:05:b3:b9:cdGoogle Internet Authority (1024 bits) (cert), not found in ca-certificates package.
$ python http_pins.py AbkhxY0L343gKf+cki7NVWp+ozk= SPKI fingerprint (sha1): 01:b9:21:c5:8d:0b:df:8d:e0:29:ff:9c:92:2e:cd:55:6a:7e:a3:39Google Internet Authority (2048 bits), not found in ca-certificates package and I was unable to find it elsewhere.
$ python http_pins.py SOZo+SvSspXXR9gjIBBPM5iQn9Q= SPKI fingerprint (sha1): 48:e6:68:f9:2b:d2:b2:95:d7:47:d8:23:20:10:4f:33:98:90:9f:d4Equifax Secure CA (cert), found in ca-certificates package.
Adding your own public key hashes?
Here are the SHA1 hashes of public keys of CAcert's root and class3 certificates, the CA I have chosen:$ python http_pins.py root.crt class3.crt /tmp/root.crt: * SPKI fingerprint (sha1): 10:da:62:4d:ef:41:a3:04:6d:cd:ba:3d:01:8f:19:df:3d:c9:a0:7c * SPKI fingerprint (sha256): 6f:28:51:40:9d:71:05:04:a3:51:15:ab:cb:9a:6d:d3:f2:57:7e:c9:37:c9:ef:19:38:92:6f:a8:2f:d6:ff:5d /tmp/class3.crt: * SPKI fingerprint (sha1): f0:61:d8:3f:95:8f:4d:78:b1:47:b3:13:39:97:8e:a9:c2:51:ba:9b * SPKI fingerprint (sha256): bd:0d:07:29:6b:43:fa:e0:3b:64:e6:50:cb:d1:8f:5e:26:71:42:52:03:51:89:d3:e1:26:3e:48:14:b4:da:5a Public-Key-Pins: max-age=600; pin-sha1=ENpiTe9BowRtzbo9AY8Z3z3JoHw=; pin-sha256=byhRQJ1xBQSjURWry5pt0/JXfsk3ye8ZOJJvqC/W/10=; pin-sha1=8GHYP5WPTXixR7MTOZeOqcJRups=; pin-sha256=vQ0HKWtD+uA7ZOZQy9GPXiZxQlIDUYnT4SY+SBS02lo=
Then on HSTS tab, I choose "Add domain":
- Domain: stalkr.net
- Include subdomains: no
- Public key fingerprints: sha1/ENpiTe9BowRtzbo9AY8Z3z3JoHw=, sha1/8GHYP5WPTXixR7MTOZeOqcJRups=
Unfortunately, net-internals HSTS tab is just a debugging interface and browsing to https://stalkr.net will reset the pubkey_hashes field. According to Adam, this is intended behaviour to allow sites to unpin themselves if pinning via HSTS is implemented. So right now, the only solution to pin public keys of CAs signing your website certificates would be to contact Chromium team to be included in the code.
I am looking forward pinning via HSTS (or any other solution) so that even small sites can enjoy pinning without requiring to be hardcoded in Chrome source.
Update: Tor Project just got pinned, and according to Chris Evans (@scarybeasts), Chris Palmer (@nocombat) "is looking at permitting sites to pin themeselves via an HTTP header, but it's hairy (think of the failure modes!)". Awesome!
Update: RFC draft is published (I updated the script above to reflect the new format). The backup key requirement is a bit annoying.
Update: it's now live in Chrome 18!
Conclusion
HSTS preloading is good to ensure your site is HTTPS only, and will never be contacted in HTTP. Public key pinning helps increase security in the broken world of SSL/TLS certificates: only listed CAs can sign certificates for the domain, not all CAs in the key store.Other interesting projects:
- DNSSEC authenticated HTTPS in Chrome 14
- Perspectives
- Convergence another approach similar to Perspectives
- CertPatrol to monitor HTTPS certificates
- EFF's HTTPS Everywhere
- EFF's SSL Observatory
- and more?
Conditional for header insertion cites wrong module. Should be mod_headers.
ReplyDeleteGood catch thank you, fixed.
ReplyDeletewww.google.com actually has OPPORTUNISTIC instead of STRICT for the mode. Didn't find any information on that mode...
ReplyDeleteSince Chromium is open source, you can have more information on this by looking at the source code.
ReplyDeleteThe chrome://net-internals/#hsts page telling you "OPPORTUNISTIC" is done by this code: http://code.google.com/searchframe#OAMlx_jo-ck/src/chrome/browser/resources/net_internals/hsts_view.js&exact_package=chromium&q=opportunistic&type=cs&l=163
Then you can view the code describing what is this mode "1": http://code.google.com/searchframe#OAMlx_jo-ck/src/net/base/transport_security_state.h&exact_package=chromium&q=DomainState&type=cs&l=51 it's just the default.
So opportunistic means HSTS is not enforced: this means you can reach google.com without https - which is true.
However, try to look up mail.google.com, it is in mode STRICT meaning HSTS is enforced: you cannot access it without https.
https://github.com/StalkR/misc/blob/master/http_pins.py not found.
ReplyDeleteAny chance you'd mind resharing? I'm trying to implement cert pinning in C#, but I don't think I'm hashing the SPKI properly. I'm having trouble generating a matching hash for Equifax Secure CA.
Any help would be greatly appreciated.
Public Key Pinning can be a problem for visitors with wrong dates on their computers. See https://www.dnswatch.info/articles/public-key-pinning-date-problem
ReplyDeleteThe draft needs to be reworked.
https://www.youtube.com/watch?v=pWdd6_ZxX8c
DeleteI think it's working as intended that things break if you're date setting is wrong. Date is important for this stuff that expires, and that's why we have automatic ways to set it like ntp.
Now ntp isn't with its own issues, and that's an interesting topic to fix.