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"解释了报告引脚验证失败的位置。

仅报告

除了使用 Public-Key-Pins 标头,您还可以使用 Public-Key-Pins-Report-Only 标头。 此标头仅将报告发送到标头中指定的 report-uri,并且即使违反固定,仍允许浏览器连接到网络服务器。

设置您的网络服务器以包含 HPKP 标头
传递 HPKP 标头所需的具体步骤取决于您使用的 Web 服务器。

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 有可能将用户锁定很长时间! 建议使用备份证书和/或固定 CA 证书。

Apache
在您的网络服务器的配置中添加类似于以下内容的行将在您的 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>

参考

HTTP Public Key Pinning (HPKP)
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Public_Key_Pinning

完全COPY

 

ChiuYut

2021年9月22日

发布者

ChiuYut

咦?我是谁?这是什么地方? Ya ha!我是ChiuYut!这里是我的小破站!