HTTP Public Key Pinning (HPKP)

HTTP公钥锁定(HPKP)是一种安全功能,它告诉Web客户端将特定加密公钥与某个Web服务器相关联,以降低使用伪造证书进行MITM攻击的风险。

为确保TLS会话中使用的服务器公钥的真实性,此公钥将包装到X.509证书中,该证书通常由证书颁发机构(CA)签名。诸如浏览器之类的Web客户端信任许多这些CA,它们都可以为任意域名创建证书。如果攻击者能够攻击单个CA,则他们可以对各种TLS连接执行MITM攻击。 HPKP可以通过告知客户端哪个公钥属于某个Web服务器来规避HTTPS协议的这种威胁。

HPKP是首次使用信任(TOFU)技术。 Web服务器第一次通过特殊的HTTP标头告诉客户端哪些公钥属于它,客户端会在给定的时间段内存储此信息。当客户端再次访问服务器时,它希望证书链中至少有一个证书包含一个公钥,其指纹已通过HPKP已知。如果服务器提供未知的公钥,则客户端应向用户发出警告。

 

Firefox和Chrome禁用固定主机的引脚验证,其验证的证书链终止于用户定义的信任锚(而不是内置信任锚)。 这意味着对于导入自定义根证书的用户,将忽略所有固定违规。

启用 HPKP

要为您的站点启用此功能,您需要在通过HTTPS访问站点时返回Public-Key-Pins HTTP标头:

Public-Key-Pins: pin-sha256="base64=="; max-age=expireTime [; includeSubDomains][; report-uri="reportURI"]
pin-sha256
引用的字符串是Base64编码的主题公钥信息(SPKI)指纹。 可以为不同的公钥指定多个引脚。 某些浏览器将来可能允许使用其他哈希算法而不是SHA-256。 请参阅下文,了解如何从证书或密钥文件中提取此信息。
max-age
浏览器应记住仅使用其中一个已定义的密钥访问此站点的时间(以秒为单位)。
includeSubDomains 可选
如果指定了此可选参数,则此规则也适用于所有站点的子域。
report-uri 可选
如果指定了此可选参数,则会将引脚验证失败报告给给定的URL。

注意 :当前规范要求包含第二个用于备份密钥的引脚,该引脚尚未在生产中使用。 这允许更改服务器的公钥,而不会破坏已经记下引脚的客户端的可访问性。 例如,当前一个密钥被泄露时,这很重要。

提取Base64编码的公钥信息

注意:虽然下面的示例显示了如何在服务器证书上设置引脚,但建议将引脚放在颁发服务器证书的CA的中间证书上,以简化证书续订和轮换。

 

首先,您需要从证书或密钥文件中提取公钥信息,并使用Base64对其进行编码。

以下命令将帮助您从密钥文件,证书签名请求或证书中提取Base64编码信息。

 

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

以下命令将提取网站的Base64编码信息。

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

HPKP 头示例

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

在此示例中,pin-sha256 =“cUPcTAZWKaASuYWhhneDttWpY3oBAkE3h2 + soZS7sWs =”固定服务器在生产中使用的公钥。 第二个引脚声明引脚-sha256 =“M8HztCzM3elUxkcjR2S5P4hhyBNf6lHkmjAHKhpGPWE =”也固定备份密钥。 max-age = 5184000告诉客户端将此信息存储两个月,根据IETF RFC,这是一个合理的时间限制。 此密钥固定也适用于所有子域,includeSubDomains声明告知。 最后,report-uri =“https://www.example.net/hpkp-report”解释了报告引脚验证失败的位置。

Report-Only header

Instead of using a Public-Key-Pins header you can also 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.

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 recommended.

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' always;

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>

Specifications

Specification Title
RFC 7469, section 2.1: Public-Key-Pins Public Key Pinning Extension for HTTP

Browser compatibility

Update compatibility data on GitHub
DesktopMobile
ChromeEdgeFirefoxInternet ExplorerOperaSafariAndroid webviewChrome for AndroidFirefox for AndroidOpera for AndroidSafari on iOSSamsung Internet
Public-Key-PinsChrome No support ? — 72Edge No support No
Notes
No support No
Notes
Notes Under consideration for future release.
Firefox Full support 35IE No support NoOpera Full support YesSafari No support NoWebView Android Full support YesChrome Android No support ? — 72Firefox Android Full support 35Opera Android Full support YesSafari iOS No support NoSamsung Internet Android Full support Yes
report-uriChrome No support 46 — 72Edge No support NoFirefox No support No
Notes
No support No
Notes
Notes See bug 1091176.
IE No support NoOpera Full support 33Safari No support NoWebView Android Full support YesChrome Android No support ? — 72Firefox Android No support NoOpera Android Full support 33Safari iOS No support NoSamsung Internet Android Full support Yes

Legend

Full support  
Full support
No support  
No support
See implementation notes.
See implementation notes.

See also