非常教程

HTTP参考手册

指南 | Guides

Public Key Pinning

公共密钥钢钉扩展HTTP(HPKP)是一种安全功能,它讲述了一个Web客户端与某个web服务器的特定密码公钥关联到降低风险中间人伪造证书的攻击。

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

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

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

启用HPKP

要为您的站点启用此功能,您需要在Public-Key-Pins通过HTTPS访问站点时返回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 =”固定生产中使用的服务器公钥。第二个引脚声明pin-sha256 =“M8HztCzM3elUxkcjR2S5P4hhyBNf6lHkmjAHKhpGPWE =”也引脚备份密钥。max-age = 5184000告诉客户将这些信息存储两个月,根据IETF RFC这是一个合理的时间限制。此关键固定也适用于所有子域,这由includeSubDomains声明告知。最后,report-uri =“ https://www.example.net/hpkp-report解释报告引脚验证失败的位置。

仅限报告的标题

Public-Key-Pins您也可以使用Public-Key-Pins-Report-Only标题,而不是使用标题。此标题仅将报告发送到report-uri标题中指定的报告,并且仍允许浏览器连接到Web服务器,即使违反了固定也是如此。

设置您的网络服务器以包含HPKP标题

提供HPKP头文件所需的具体步骤取决于您使用的Web服务器。

注意:这些示例使用两个月的最大年龄并包含所有子域。建议验证此设置适用于您的服务器。

如果使用不当,HPKP可能会长时间锁定用户!建议使用备份证书和/或固定CA证书。

阿帕奇

在您的web服务器的配置文件中添加一行类似于以下内容的行将启用Apache上的HPKP。这需要mod_headers启用。

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

Nginx的

添加以下行并插入适当的pin-sha256="..."值将在您的nginx上启用HPKP。这需要ngx_http_headers_module.

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

Lighttpd

以下与相关密钥信息(pin-sha256 =“...”字段)的行将启用lighttpd上的HPKP。

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

注意:这需要mod_setenv加载server.module,如果尚未加载,可以将其包含在内。

server.modules += ( "mod_setenv" )

IIS

将以下行添加到Web.config文件以发送Public-Key-Pins标头:

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

产品规格

规范

标题

RFC 7469,第2.1节:公钥索引

HTTP的公钥固定扩展

浏览器兼容性

Feature

Chrome

Edge

Firefox

Internet Explorer

Opera

Safari

Basic Support

(Yes)

No1

35.0

?

(Yes)

?

report-uri

46

?

No2

?

33

?

Feature

Android

Chrome for Android

Edge mobile

Firefox for Android

IE mobile

Opera Android

iOS Safari

Basic Support

(Yes)

(Yes)

?

35.0

?

(Yes)

?

report-uri

(Yes)

(Yes)

?

No

?

33

?

  1. 正考虑未来发布。
  1. 参见Bugzilla错误1091176。