Please note, this is a STATIC archive of website developer.mozilla.org from 03 Nov 2016, cach3.com does not collect or store any user information, there is no "phishing" involved.

Revision 1055706 of Public Key Pinning

  • Revision slug: Web/Security/Public_Key_Pinning
  • Revision title: Public Key Pinning
  • Revision id: 1055706
  • Created:
  • Creator: rugk
  • Is current revision? No
  • Comment minor improvments

Revision Content

The Public Key Pinning Extension for HTTP (HPKP) is a security feature that tells a web client to associate a specific cryptographic public key with a certain web server to prevent MITM attacks with forged certificates.

Note: The Public Key Pinning described here is different from the limited preload list based key pinning introduced in Firefox 32.

To ensure the authenticity of a server's public key used in TLS sessions, this public key is wrapped into a X.509 certificate which is usually signed by a certificate authority (CA). Web clients such as browsers trust a lot of these CAs, which can all create certificates for arbitrary domain names. If an attacker is able to compromise a single CA, they can perform MITM attacks on various TLS connections. HPKP can circumvent this threat for the HTTPS protocol by telling the client which public key belongs to a certain web server.

HPKP is a Trust on First Use (TOFU) technique. The first time a web server tells a client via a special HTTP header which public keys belong to it, the client stores this information for a given period of time. When the client visits the server again, it expects at least one certificate in the certificate chain to contain a public key whose fingerprint is already known via HPKP. If the server delivers an unknown public key, the client should present a warning to the user.

Firefox (and Chrome) disable Pin Validation for Pinned Hosts whose validated certificate chain terminates at a user-defined trust anchor (rather than a built-in trust anchor). This means that for users who imported custom root certificates all pinning violations are ignored.

Enabling HPKP

Enabling this feature for your site is as simple as returning the Public-Key-Pins HTTP header when your site is accessed over HTTPS:

Public-Key-Pins: pin-sha256="base64=="; max-age=expireTime [; includeSubdomains][; report-uri="reportURI"]
pin-sha256
The quoted string is the Base64 encoded Subject Public Key Information (SPKI) fingerprint. It is possible to specify multiple pins for different public keys. Some browsers might allow other hashing algorithms than SHA-256 in the future. See below on how to extract this information out of a certificate or key file.
max-age
The time, in seconds, that the browser should remember that this site is only to be accessed using one of the defined keys.
includeSubdomains {{ optional_inline() }}
If this optional parameter is specified, this rule applies to all of the site's subdomains as well.
report-uri {{ optional_inline() }}
If this optional parameter is specified, pin validation failures are reported to the given URL.

Note: The current specification requires including a second pin for a backup key which isn't yet used in production. This allows for changing the server's public key without breaking accessibility for clients that have already noted the pins. This is important for example when the former key gets compromised.

 

Extracting the Base64 encoded public key information

Note: While the example below shows how to set a pin on a server certificate, it is recommended to place the pin on the intermediate certificate of the CA that issued the server certificate, to ease certificates renewals and rotations.

First you need to extract the public key information from your certificate or key file and encode them using Base64.

The following commands will help you extract the Base64 encoded information from a key file, a certificate signing request, or a certificate.

openssl rsa -in my-key-file.key -outform der -pubout | openssl dgst -sha256 -binary | openssl enc -base64
openssl req -in my-signing-request.csr -pubkey -noout | openssl rsa -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64
openssl x509 -in my-certificate.crt -pubkey -noout | openssl rsa -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64

The following command will extract the Base64 encoded information for a website.

openssl s_client -servername www.example.com -connect www.example.com:443 | openssl x509 -pubkey -noout | openssl rsa -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64

Example HPKP Header

Public-Key-Pins: pin-sha256="cUPcTAZWKaASuYWhhneDttWpY3oBAkE3h2+soZS7sWs="; pin-sha256="M8HztCzM3elUxkcjR2S5P4hhyBNf6lHkmjAHKhpGPWE="; max-age=5184000; includeSubdomains; report-uri="https://www.example.net/hpkp-report"

In this example, pin-sha256="cUPcTAZWKaASuYWhhneDttWpY3oBAkE3h2+soZS7sWs=" pins the server's public key used in production. The second pin declaration pin-sha256="M8HztCzM3elUxkcjR2S5P4hhyBNf6lHkmjAHKhpGPWE=" also pins the backup key. max-age=5184000 tells the client to store this information for two month, which is a reasonable time limit according to the IETF RFC. This key pinning is also valid for all subdomains, which is told by the includeSubdomains declaration. Finally, report-uri="https://www.example.net/hpkp-report" explains where to report pin validation failures.

Report-Only header

Instead of using a Public-Key-Pins header you can alos use a Public-Key-Pins-Report-Only header. This header only sends reports to the report-uri specified in the header and does still allow browsers to connect to the webserver even if the pinning is violated.

Public-Key-Pins-Report-Only: pin-sha256="cUPcTAZWKaASuYWhhneDttWpY3oBAkE3h2+soZS7sWs="; pin-sha256="M8HztCzM3elUxkcjR2S5P4hhyBNf6lHkmjAHKhpGPWE="; max-age=5184000; includeSubdomains; report-uri="https://www.example.net/hpkp-report"

Setting up your webserver to include the HPKP header

The concrete steps necessary to deliver the HPKP header depend on the web server you use.

Note: These examples use a max-age of two months and include all subdomains. It is advised to verify that this setup will work for your server.

HPKP has the potential to lock out users for a long time if used incorrectly! The use of backup certificates and/or pinning the CA certificate is recommend.

Apache

Adding a line similar to the following to your webserver's config will enable HPKP on your Apache. This requires mod_headers enabled.

Header always set Public-Key-Pins "pin-sha256=\"base64+primary==\"; pin-sha256=\"base64+backup==\"; max-age=5184000; includeSubDomains"

Nginx

Adding the following line and inserting the appropriate pin-sha256="..." values will enable HPKP on your nginx. This requires the ngx_http_headers_module.

add_header Public-Key-Pins 'pin-sha256="base64+primary=="; pin-sha256="base64+backup=="; max-age=5184000; includeSubDomains';

Lighttpd

The following line with your relevant key information (pin-sha256="..." fields) will enable HPKP on lighttpd.

setenv.add-response-header  = ( "Public-Key-Pins" => "pin-sha256=\"base64+primary==\"; pin-sha256=\"base64+backup==\"; max-age=5184000; includeSubDomains")

Note: This requires the mod_setenv server.module loaded which can be included by the following if not already loaded.

server.modules += ( "mod_setenv" )

IIS

Add the following line to the Web.config file to send the Public-Key-Pins header:

<system.webServer>
  ...

  <httpProtocol>
    <customHeaders>
      <add name="Public-Key-Pins" value="pin-sha256=&quot;base64+primary==&quot;; pin-sha256=&quot;base64+backup==&quot;; max-age=5184000; includeSubDomains" />
    </customHeaders>
  </httpProtocol>

  ...
</system.webServer>

Browser compatibility

{{ CompatibilityTable() }}

Feature Chrome Firefox (Gecko) Internet Explorer Opera Safari
Basic support {{ CompatChrome("46.0") }} [1] {{ CompatGeckoDesktop("35") }} {{ CompatNo()}} {{ CompatUnknown() }} {{ CompatUnknown() }}
Feature Chrome for Android Firefox Mobile (Gecko) IE Mobile Opera Mobile Safari Mobile
Basic support {{ CompatUnknown() }} {{ CompatGeckoMobile("35") }} {{CompatUnknown()}} {{ CompatUnknown() }} {{ CompatUnknown() }}

[1] Behind a flag in Chrome 38.

Specification

See Also

Revision Source

<p class="summary"><span class="seoSummary">The <strong>Public Key Pinning Extension for HTTP</strong> (HPKP) is a security feature that tells a web client to associate a specific cryptographic public key with a certain web server to prevent <a href="https://en.wikipedia.org/wiki/Man-in-the-middle_attack">MITM</a> attacks with forged certificates.</span></p>

<div class="note">
<p><strong>Note:</strong> The Public Key Pinning described here is different from the limited <a href="https://monica-at-mozilla.blogspot.de/2014/08/firefox-32-supports-public-key-pinning.html">preload list based key pinning</a> introduced in Firefox 32.</p>
</div>

<p>To ensure the authenticity of a server's public key used in TLS sessions, this public key is wrapped into a X.509 certificate which is usually signed by a certificate authority (CA). Web clients such as browsers trust a lot of these CAs, which can all create certificates for arbitrary domain names. If an attacker is able to compromise a single CA, they can perform MITM attacks on various TLS connections. HPKP can circumvent this threat for the <a href="/en-US/docs/Glossary/https">HTTPS</a> protocol by telling the client which public key belongs to a certain web server.</p>

<p>HPKP is a <em>Trust on First Use</em> (TOFU) technique. The first time a web server tells a client via a special HTTP header which public keys belong to it, the client stores this information for a given period of time. When the client visits the server again, it expects at least one certificate in the certificate chain to contain a public key whose fingerprint is already known via HPKP. If the server delivers an unknown public key, the client should present a warning to the user.</p>

<p class="note">Firefox (and Chrome)<strong> disable Pin Validation</strong> for Pinned Hosts whose validated certificate chain terminates at a <strong>user-defined trust anchor</strong> (rather than a built-in trust anchor). This means that for users who imported custom root certificates all pinning violations are ignored.</p>

<h2 id="Enabling_HPKP">Enabling HPKP</h2>

<p>Enabling this feature for your site is as simple as returning the <code>Public-Key-Pins</code> HTTP&nbsp;header when your site is accessed over HTTPS:</p>

<pre>
Public-Key-Pins: pin-sha256="base64=="; max-age=<em>expireTime</em> [; includeSubdomains][; report-uri="<em>reportURI"</em>]
</pre>

<dl>
 <dt><code>pin-sha256</code></dt>
 <dd>The quoted string is the Base64 encoded <em>Subject Public Key Information</em> (SPKI) fingerprint. It is possible to specify multiple pins for different public keys. Some browsers might allow other hashing algorithms than SHA-256 in the future. See below on how to extract this information out of a certificate or key file.</dd>
 <dt><code>max-age</code></dt>
 <dd>The time, in seconds, that the browser should remember that this site is only to be accessed using one of the defined keys.</dd>
 <dt><code>includeSubdomains</code> {{ optional_inline() }}</dt>
 <dd>If this optional parameter is specified, this rule applies to all of the site's subdomains as well.</dd>
 <dt><code>report-uri</code> {{ optional_inline() }}</dt>
 <dd>If this optional parameter is specified, pin validation failures are reported to the given URL.</dd>
</dl>

<div class="note">
<p><strong>Note:</strong> The current specification requires including a second pin for a backup key which isn't yet used in production. This allows for changing the server's public key without breaking accessibility for clients that have already noted the pins. This is important for example when the former key gets compromised.</p>
</div>

<div class="note">
<p><strong>Note:</strong> Firefox doesn't support HTTP Public Key Pinning violation reporting yet. Chrome support reporting from the version 46.</p>

<ul>
 <li>Firefox: <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1091176">Bug 1091176 - Implement report-uri directive for HPKP </a> and <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=787133">Bug 787133 - (hpkp) Implement Public Key Pinning Extension for HTTP (HPKP)</a></li>
 <li>Chrome:&nbsp;<a href="https://developers.google.com/web/updates/2015/09/HPKP-reporting-with-chrome-46">https://developers.google.com/web/updates/2015/09/HPKP-reporting-with-chrome-46</a> , <a href="https://www.chromestatus.com/feature/4669935557017600">HTTP Public Key Pinning violating reporting</a> and <a href="https://code.google.com/p/chromium/issues/detail?id=445793"> Issue 445793: HPKP Reporting on invalid pins</a></li>
</ul>
</div>

<p id="sect1">&nbsp;</p>

<h3 id="Extracting_the_Base64_encoded_public_key_information">Extracting the Base64 encoded public key information</h3>

<div class="note">
<p><strong>Note:</strong> While the example below shows how to set a pin on a server certificate, it is recommended to place the pin on the intermediate certificate of the CA that issued the server certificate, to ease certificates renewals and rotations.</p>
</div>

<p>First you need to extract the public key information from your certificate or key file and encode them using Base64.</p>

<p>The following commands will help you extract the Base64 encoded information from a key file, a certificate signing request, or a certificate.</p>

<pre>
<code>openssl rsa -in my-key-file.key -outform der -pubout | openssl dgst -sha256 -binary | </code>openssl enc -base64</pre>

<pre>
<code>openssl req -in my-signing-request.csr -pubkey -noout | openssl rsa -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64</code></pre>

<pre>
<code>openssl x509 -in my-certificate.crt -pubkey -noout | openssl rsa -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64</code></pre>

<p>The following command will extract the Base64 encoded information for a website.</p>

<pre>
<code>openssl s_client </code>-servername www.example.com <code>-connect www.example.com:443 | openssl x509 -pubkey -noout | openssl rsa -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64</code></pre>

<h3 id="Example_HPKP_Header">Example HPKP Header</h3>

<pre>
Public-Key-Pins: pin-sha256="cUPcTAZWKaASuYWhhneDttWpY3oBAkE3h2+soZS7sWs="; pin-sha256="M8HztCzM3elUxkcjR2S5P4hhyBNf6lHkmjAHKhpGPWE="; max-age=5184000; includeSubdomains; report-uri="<em>https://www.example.net/hpkp-report"</em></pre>

<p>In this example, <strong>pin-sha256="cUPcTAZWKaASuYWhhneDttWpY3oBAkE3h2+soZS7sWs="</strong> pins the server's public key used in production. The second pin declaration <strong>pin-sha256="M8HztCzM3elUxkcjR2S5P4hhyBNf6lHkmjAHKhpGPWE="</strong> also pins the backup key. <strong>max-age=5184000</strong> tells the client to store this information for two month, which is a reasonable time limit according to the IETF RFC. This key pinning is also valid for all subdomains, which is told by the <strong>includeSubdomains</strong> declaration. Finally, <strong>report-uri="https://www.example.net/hpkp-report"</strong> explains where to report pin validation failures.</p>

<h4 id="Report-Only_header">Report-Only header</h4>

<p>Instead of using a Public-Key-Pins header you can alos use a Public-Key-Pins-Report-Only header. This header only sends reports to the report-uri specified in the header and does still allow browsers to connect to the webserver even if the pinning is violated.</p>

<pre>
<code>Public-Key-Pins-Report-Only</code>: pin-sha256="cUPcTAZWKaASuYWhhneDttWpY3oBAkE3h2+soZS7sWs="; pin-sha256="M8HztCzM3elUxkcjR2S5P4hhyBNf6lHkmjAHKhpGPWE="; max-age=5184000; includeSubdomains; report-uri="<em>https://www.example.net/hpkp-report"</em></pre>

<h3 id="Setting_up_your_webserver_to_include_the_HPKP_header">Setting up your webserver to include the HPKP header</h3>

<p>The concrete steps necessary to deliver the HPKP header depend on the web server you use.</p>

<div class="note">
<p><strong>Note:</strong> These examples use a max-age of two months and include all subdomains. It is advised to verify that this setup will work for your server.</p>
</div>

<div class="warning">
<p id="HPKP_has_the_potential_to_lock_out_users_for_a_long_time_if_used_incorrectly!_The_use_of_backup_certificates_andor_pinning_the_CA_certificate_is_recommend.">HPKP has the potential to lock out users for a long time if used incorrectly! The use of backup certificates and/or pinning the CA certificate is recommend.</p>
</div>

<h4 id="Apache">Apache</h4>

<p>Adding a line similar to the following to your webserver's config will enable HPKP on your Apache. This requires <code>mod_headers</code> enabled.</p>

<pre>
Header always set Public-Key-Pins "pin-sha256=\"base64+primary==\"; pin-sha256=\"base64+backup==\"; max-age=5184000; includeSubDomains"
</pre>

<h4 id="Nginx">Nginx</h4>

<p>Adding the following line and inserting the appropriate <code>pin-sha256="..."</code> values will enable HPKP on your nginx. This requires the <code>ngx_http_headers_module.</code></p>

<pre>
add_header Public-Key-Pins 'pin-sha256="base64+primary=="; pin-sha256="base64+backup=="; max-age=5184000; includeSubDomains';</pre>

<h4 id="Lighttpd">Lighttpd</h4>

<p>The following line with your relevant key information (pin-sha256="..." fields) will enable HPKP on lighttpd.</p>

<pre>
setenv.add-response-header&nbsp; = ( "Public-Key-Pins" =&gt; "pin-sha256=\"base64+primary==\"; pin-sha256=\"base64+backup==\"; max-age=5184000; includeSubDomains")</pre>

<p><strong>Note:</strong> This requires the <code>mod_setenv</code> server.module loaded which can be included by the following if not already loaded.</p>

<pre>
<code>server.modules += ( "mod_setenv" )</code></pre>

<h4 id="IIS">IIS</h4>

<p>Add the following line to the Web.config file to send the <code>Public-Key-Pins</code> header:</p>

<pre class="brush: xml">
&lt;system.webServer&gt;
  ...

  &lt;httpProtocol&gt;
    &lt;customHeaders&gt;
      &lt;add name="Public-Key-Pins" value="pin-sha256=&amp;quot;base64+primary==&amp;quot;; pin-sha256=&amp;quot;base64+backup==&amp;quot;; max-age=5184000; includeSubDomains" /&gt;
    &lt;/customHeaders&gt;
  &lt;/httpProtocol&gt;

  ...
&lt;/system.webServer&gt;
</pre>

<h2 id="Browser_compatibility">Browser compatibility</h2>

<p>{{ CompatibilityTable() }}</p>

<div id="compat-desktop">
<table class="compat-table">
 <tbody>
  <tr>
   <th>Feature</th>
   <th>Chrome</th>
   <th>Firefox (Gecko)</th>
   <th>Internet Explorer</th>
   <th>Opera</th>
   <th>Safari</th>
  </tr>
  <tr>
   <td>Basic support</td>
   <td>{{ CompatChrome("46.0") }} [1]</td>
   <td>{{ CompatGeckoDesktop("35") }}</td>
   <td>{{ CompatNo()}}</td>
   <td>{{ CompatUnknown() }}</td>
   <td>{{ CompatUnknown() }}</td>
  </tr>
 </tbody>
</table>
</div>

<div id="compat-mobile">
<table class="compat-table">
 <tbody>
  <tr>
   <th>Feature</th>
   <th>Chrome for Android</th>
   <th>Firefox Mobile (Gecko)</th>
   <th>IE Mobile</th>
   <th>Opera Mobile</th>
   <th>Safari Mobile</th>
  </tr>
  <tr>
   <td>Basic support</td>
   <td>{{ CompatUnknown() }}</td>
   <td>{{ CompatGeckoMobile("35") }}</td>
   <td>{{CompatUnknown()}}</td>
   <td>{{ CompatUnknown() }}</td>
   <td>{{ CompatUnknown() }}</td>
  </tr>
 </tbody>
</table>
</div>

<p>[1] Behind a flag in Chrome 38.</p>

<h2 id="Specification">Specification</h2>

<ul>
 <li><a href="https://tools.ietf.org/html/rfc7469">IETF RFC&nbsp;- Public Key Pinning Extension for HTTP</a></li>
</ul>

<h2 id="See_Also">See Also</h2>

<ul>
 <li><a href="/en-US/docs/Security/HTTP_Strict_Transport_Security">HTTP Strict Transport Security</a></li>
</ul>
Revert to this revision