HSTS and HPKP
Introduction
HTTP Strict Transport Security (HSTS) and HTTP Public Key Pinning (HPKP) are two mechanisms which can increase the security of your SME Server's web server. They should be implemented with care.
HTTP Strict Transport Security (HSTS)
HTTP Strict Transport Security is a mechanism that allows your web server to tell clients, once they have first connected securely (i.e., over HTTPS), to always connect securely (i.e., to never connect via HTTP), for a specified timeframe. HSTS can protect against man-in-the-middle attacks. Wikipedia, as usual, has considerably more detail. HSTS can be enabled by creating a simple custom template fragment on your server.
Enabling HSTS
To enable HSTS on your SME Server, you must first be using HTTPS with your web server, and should ideally have a trusted TLS certificate. See Letsencrypt for one simple and free way to obtain a certificate and keep it up-to-date. You must then create a small template fragment. To begin:
[root@e-smith ~]# mkdir -p /etc/e-smith/templates-custom/etc/httpd/conf/httpd.conf/VirtualHosts/ [root@e-smith ~]# nano -w /etc/e-smith/templates-custom/etc/httpd/conf/httpd.conf/VirtualHosts/04StrictTransportSecurity
Edit the file to contain the following lines:
### Enable HTTP Strict Transport Security, lifetime 6 months ### Header always add Strict-Transport-Security "max-age=15768000; includeSubDomains; preload" env=HTTPS
The value of max-age is in seconds, so the value given corresponds to 6 months. After a client has visited your server using HTTPS once, it will always use HTTPS for subsequent visits within that timeframe. You may adjust this time if you wish. Press Ctrl-X to exit, and Y to save. Then, expand the template and restart your web server:
[root@e-smith ~]# expand-template /etc/httpd/conf/httpd.conf [root@e-smith ~]# service httpd-e-smith restart
Validating HSTS
You can analyze your security headers at https://securityheaders.io/.
HTTP Public Key Pinning (HPKP)
HTTP Public Key Pinning (HPKP) lets you specify which cryptographic identities should be accepted by clients to authenticate your web server. This is done by providing cryptographic hashes of one or more public keys as part of the response headers. As with HSTS, these are cached by the client for a specified period of time. Within that time, if the client attempts to connect, and a certificate is provided which does not match one of the specified hashes, the connection will be rejected. HPKP protects against impersonation of your website using mis-issued or otherwise fraudulent certificates. You can specify a URI to which failures can be reported, if desired. HPKP is supported by Firefox and Chrome, but not Internet Explorer.
Enabling HPKP
Enabling HPKP is more complicated than enabling HSTS. You must determine which public keys to pin, obtain the hashes for those keys, subscribe to a reporting service if desired, and then create a template fragment with this information.
Which Public Keys to Pin
You must pin at least two public keys when using HPKP: one that is currently being used, and a backup that isn't currently being used in production. This determination is critical--if the certificate chain served by your server does not contain at least one of the public keys that are pinned, the client's connection will fail. A full discussion of which keys to pin is outside the scope of this How-to, but there is a good deal of information on the web on this subject. See this article for one example.
Obtaining Hashes
You can extract the hash information from a private key, from a certificate signing request (CSR), or from an X509 certificate. To extract from a private key, run
openssl rsa -in my-key-file.key -outform der -pubout | openssl dgst -sha256 -binary | openssl enc -base64
To extract from a CSR, run
openssl req -in my-signing-request.csr -pubkey -noout | openssl rsa -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64
And to extract from a certificate, run
openssl x509 -in my-certificate.crt -pubkey -noout | openssl rsa -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64
The output will look like this:
[root@e-smith ~]# openssl rsa -in my-key-file.key -outform der -pubout | openssl dgst -sha256 -binary | openssl enc -base64 writing RSA key 6/l2FO9X6N4kewCM7IGsaS0ib2KIQxpsVn4k6T19/iQ= [root@e-smith ~]#
The string beginning "6/l2..." is the SHA256 hash of the public key associated with this private key. Save this output, and repeat for whatever other keys you want to pin.
Reporting Service
User agents (i.e., web browsers) can report failed connection attempts to a third-party reporting service, if such a service is specified in the headers. One no-cost service is provided by https://report-uri.io. After you register for their services, they will provide a unique URI which can be included in your headers.
Creating the Template Fragment
Once you've determined which keys to use, obtained hashes for them, and subscribed to a reporting service if desired, you can create the template fragment to implement HPKP.
[root@e-smith ~]# mkdir -p /etc/e-smith/templates-custom/etc/httpd/conf/httpd.conf/VirtualHosts/ [root@e-smith ~]# nano -w /etc/e-smith/templates-custom/etc/httpd/conf/httpd.conf/VirtualHosts/05PublicKeyPinning
Edit the file to contain the following lines, replacing the values for pin-sha256 with the hashes you determined above, and adding or removing pin-sha256 lines as required:
### Enable HTTP Public Key Pinning, lifetime 24 hours Header always set Public-Key-Pins \ "pin-sha256=\"5q8u3+ik8DtXToj8Bazgk3lptrL/loAbkb1cpG1y+vU=\"; \ pin-sha256=\"6/l2FO9X6N4kewCM7IGsaS0ib2KIQxpsVn4k6T19/iQ=\"; \ pin-sha256=\"57+cQbXlCRtm3+Ooigd1DrLwqZOr1AC/lrJGz7INwrA=\"; \ max-age=86400; includeSubdomains; \ report-uri=\"https://report-uri.io/report/reallylongnumber/reportOnly\""
As above, the max-age is given in seconds. The value used above corresponds to 24 hours. In production, a longer value should be used, probably on the order of 30-60 days. For testing, however, a small value can be helpful to avoid long-term consequences of improper pins.
You can also request that clients only report pinning failures, without refusing to connect. It may be advisable to initially deploy with this setting, and see if any issues are being reported. To do this, replace the second line of the template fragment with the following:
Header always set Public-Key-Pins-Report-Only \
Once you're finished, expand the template and restart the web server:
[root@e-smith ~]# expand-template /etc/httpd/conf/httpd.conf [root@e-smith ~]# service httpd-e-smith restart
Validating HPKP
The tool at https://report-uri.io/home/pkp_analyse will validate that your server is serving HPKP headers, and that they are appropriate (i.e., that at least one of the hashes matches what's in your TLS certificate).