HPKP pinning

NOTE: Recently the HPKP Pinning technique has been considered to be not quite safe. Thus, Google has an intent to deprecate it. Though, no final decision has been made yet. More details can be checked here.
What is HPKP?

Public Key Pinning Extension for HTTP (HPKP) is a security mechanism developed by Google that prevents from using mistakenly or fraudulently issued certificates with a particular domain name.

It allows the server to block any certificates usage, except the ones authorized by the site owner, with a help of a special HTTP header.

Manual monitoring of all such cases requires too many resources, so this mechanism was implemented as a possible solution.

What is the reason for using HPKP?

Let’s say a company has a domain name and a certificate issued for this domain name by a Certificate Authority (for example, Comodo, now Sectigo).

As there are multiple Certificate Authorities with valid licenses in the world, any of them can receive a request from any person in the world to issue the certificate for the same domain name. This means that any person in the world can initiate a request to issue a certificate for this domain name to a Certificate Authority, not taking into account whether he has the legitimate right to do this or not. Domain Control Validation (DCV) can be one of the prevention measures, where one needs to prove that he or she owns the domain name. At the same time, as any algorithm in the Internet, it can be potentially or theoretically broken.

The HPKP technology allows avoiding using such incorrectly issued certificates for establishing the secure connection with a domain name.

The principle is the following: when establishing a secure HTTPS connection, a web -browser checks the set-up HPKP PIN (a special string of symbols based on one of the codes from the certificate chain sent by the server) indicated in the website header. If the server provides a certificate with a PIN that is different from the one provided in the header, the HPKP check fails, and the secure connection will be blocked.

During the very first client-server interaction over HTTPS a web server “'sends” this PIN to the web client along with other headers set for the website. In this way, the web client “remembers” the PIN. Each next connection includes a memorized PIN, and the web client matches it with the PINs of certificates in the provided certificate chain. If the PIN matches, a connection is established, if no match, the session is blocked.

The SHA-256 SPKI (Simple public key infrastructure) hash of the certificate is used as hash for this PIN code.

How to retrieve the correct PIN for HPKP?

If you have access to the openssl command line utility, you can use the following command to get the SPKI fingerprint from a certificate:

openssl x509 -pubkey < certificate.crt | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | base64

certificate.crt here should be replaced with the filename of the certificate file which SPKI fingerprint you want to PIN.

As an alternative, it is possible to download the certificate from your Namecheap account as described here and decode it with this convenient tool.

The input certificate.crt file is the first certificate of the CA bundle. In this case, its common name is COMODO RSA Domain Validation Secure Server CA.

Result: klO23nT2ehFDXCfx3eHTDRESMz3asj1muO+4aIdjiuY=

What source should be used for the PIN?

Pinning your own domain certificate is not the best idea. The key might change or get compromised because the web server was hacked. You might have multiple certificates in use. The key might change because you can reissue or renew your certificates. Pinning the root certificate of the Certificate Authority (CA) is also a questionable solution as a lot of CAs have multiple roots or use cross-signing with a different CA’s root.

The easiest, but not the most secure place to extract the PIN from is the first intermediate CA certificate (the first certificate code from the CA bundle. The signature of that certificate is present in your website certificate, so the PIN based on it can be confirmed for sure.

This way, when there is time for the certificate renewal, your end certificate from the same CA will have no pinning issues.

If the CA issues a different root certificate, then an issue might occur, as the PINned intermediate certificate may also change. There is no clear solution for this yet, although there is one thing you can do to mitigate this: always have a backup PIN.

The RFC states that you need to provide at least two PINs (it is possible to set up more PINs, although, it is not necessary in general). One of the PINs must be present in the certificate chain sent by the server. The other PIN is a backup one and must not be present in it. Later you can replace the current chain with the one containing the certificate with the backup PIN.

There are two valid options for the backup PIN:

a) The PIN of the intermediate certificate from a different CA can be used. The disadvantage of this option is that you will need to have a valid SSL certificate from a non-related CA.
b) An alternative and more secure solution is to create at least three separate public keys (generated and contained within three Certificate Signing Request codes) beforehand and to keep two of those keys as backups in a safe place, offline and off-site (not in a public folder on a server).

You can create SPKI hashes for three certificates and PIN them. You will need only the first key as an active one. When it is needed, you can then use one of the alternative keys. Though remember that the alternative public key should be signed by a CA, and the certificate validation and issuance process can take up to a few days depending on the certificate.

However, a delay in activation is not a problem for HPKP because we take the SPKI hash of the public key, and not of the certificate. And since the hash is not taken from the certificate authority chain of trust, expiration or change of the CA root certificate will not affect it.

If you have the means and procedures to create and securely store at least three separate keys as described above and PIN them, it will also protect you from your CA provider getting compromised and from a fake certificate being used to attack your website.

HPKP setup on Linux-based servers

Here is a quick feature overview of HPKP:

  • HPKP is set at the HTTP level using the Public-Key-Pins response header.
  • The policy retention period is set with the max-age parameter, it specifies the duration of the current PIN being cached by the web clients in seconds.
  • The PKP header can only be used over an error-free secure encryption.
  • If multiple headers are seen, only the first one is processed.
  • PINning can be extended to subdomains with the includeSubDomains parameter.
  • When a new PKP header is received, it overwrites previously stored PINs and metadata.
  • A PIN consists of the hashing algorithm and a "Subject Public Key Info" fingerprint.

The example below PINs the COMODO RSA Domain Validation Secure Server CA and the intermediate certificate of some different CA as a backup, with a 30-day expiration time including all subdomains.

Apache

In order to set up HPKP, you need to add this to your HTTPS configuration in the file where there is a virtual host for port 443 for your website:

# Optionally load the headers module: LoadModule headers_module modules/mod_headers.so

Header set Public-Key-Pins "pin-sha256=\"klO23nT2ehFDXCfx3eHTDRESMz3asj1muO+4aIdjiuY=\"; pin-sha256=\"6X0iNAQtPIjXKEVcqZBwyMcRwq1yW60549axatu3oDE=\"; max-age=2592000; includeSubDomains"

- ”pin-sha256” parameters are the main and backup PINs for HPKP. “max-age” parameter indicates for how long PINs will be cached by browsers in seconds.
- “includeSubDomains” is an optional parameter to enable HPKP for subdomains. Please specify this parameter only if you are using a wildcard certificate or similar certificates from one CA to secure the main domain and subdomains.

Nginx

NGINX is even shorter with its configuration rule. Add this to your HTTPS configuration in the file where there is a virtual host for port 443 for your website:

add_header Public-Key-Pins 'pin-sha256="klO23nT2ehFDXCfx3eHTDRESMz3asj1muO+4aIdjiuY="; pin-sha256="6X0iNAQtPIjXKEVcqZBwyMcRwq1yW60549axatu3oDE="; max-age=2592000; includeSubDomains';

Litehttpd

The lighttpd setup is quite simple. Add the following configuration to your Lighttpd configuration file (by default, it’s located in the /etc/lighttpd/lighttpd.conf file):

server.modules += ( "mod_setenv" )
$HTTP["scheme"] == "https" {

setenv.add-response-header = ( "Public-Key-Pins" => "pin-sha256=\"klO23nT2ehFDXCfx3eHTDRESMz3asj1muO+4aIdjiuY=\"; pin-sha256=\"6X0iNAQtPIjXKEVcqZBwyMcRwq1yW60549axatu3oDE=\"; max-age=2592000; includeSubDomains") }

cPanel

cPanel is a standard hosting panel that is normally installed on an Apache server. The HPKP configuration for it is the same as for an Apache server, and is set in the .htaccess file.

For the main domain of cPanel, this file is located in the public_html folder; for all other domain names, the preferred option is to use separate .htaccess files in their corresponding folders.

This file may be hidden, so it is recommended to check the “Show hidden files” option in the “Settings” menu of File Manager (normally, it is located in the upper right corner of the panel).

Also, some applications like WordPress may rewrite this file during installation, so it is better to back up your settings and add them to a new file.

Other servers: IIS

The code is added to the “HTTP Response Headers” tool in IIS Manager. The header’s name should be “Public-Key-Pins”. The syntax is such:

pin-sha256="klO23nT2ehFDXCfx3eHTDRESMz3asj1muO+4aIdjiuY=";
pin-sha256="6X0iNAQtPIjXKEVcqZBwyMcRwq1yW60549axatu3oDE="; max-age=2592000

You can check this article for more details regarding the setup.

Additional information

HPKP reporting allows the user-agent to report any failures back to you.

If you add an additional report-uri="http://*your_domain_name*/hpkp-report" parameter to the header and set up a listener there, web clients will send reports if they encounter a failure. A report is sent as a POST request to report-uri with a JSON body like this:

{
"date-time": "2014-12-26T11:52:10Z",
"hostname": "ncssltest.info",
"port": 443,
"effective-expiration-date": "2014-12-31T12:59:59",
"include-subdomains": true,
"served-certificate-chain": [
"-----BEGINCERTIFICATE-----\nMIIAuyg[...]tqU0CkVDNx\n-----ENDCERTIFICATE-----"
],
"validated-certificate-chain": [
"-----BEGINCERTIFICATE-----\nEBDCCygAwIBA[...]PX4WecNx\n-----ENDCERTIFICATE-----"
],
"known-pins": [
"pin-sha256=\"dUezRu9zOECb901Md727xWltNsj0e6qzGk\"",
"pin-sha256=\"E9CqVKB9+xZ9INDbd+2eRQozqbQ2yXLYc\""
]
}

HPKP can be set up without enforcement, in the reporting mode by using the
Public-Key-Pins-Report-Only response header.

This approach allows you to set up PINning without your site being unreachable or HPKP being configured incorrectly. You can later move to enforcement by changing the header back to Public-Key-Pins.

Updated
Viewed
22223 times

Need help? We're always here for you.

notmyip