HTTP Public Key Pinning

2017-02-13 18:57 #旧文章

小心

从 Chrome 72 开始 HPKP 支持已被完全移除。

HTTP Public Key Pinning 简称 HPKP

本文翻译自 MDN,原文版本为 Jan 31, 2017, 11:00:31 PM

HPKP 是一个 HTTP 安全扩展,它告诉网络客户端服务器公钥的哈希值以减少中间人伪造证书攻击的风险。

为了确保在 TLS 会话中服务器公钥的真实性,公钥应该使用被 CA 数字证书认证机构签名的 X.509 证书进行包装。网络用户例如浏览器信任许多这样的 CA,这些 CA 可以为任意的域名创建证书。如果一个黑客能够控制一个 CA,他们就可以在多样的 TLS 链接中发起中间人攻击。HPKP 可以告诉客户端哪一个公钥属于真正的网站服务器以规避这样的风险。

HPKP 是一个『信任第一次使用』Trust on First Use 的技术。第一次服务器通过一个特殊的 HTTP 响应头告诉客户端哪一个公钥属于这个服务器,客户端则会在给定的时间内记住这些信息。当这个客户端再一次访问这一个服务器时,它会期望在服务器提供的证书链中找到一个指纹与通过 HPKP 得到的指纹相同的证书。如果这个服务器提供了一个未知的公钥,这个客户端应该显示一个警告给用户。

警告

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).
这意味着如果用户导入了一个自定义的根证书,那么所有已经固定了的公钥都会被 Firefox 和 Chrome 忽视。

启用 HPKP

想要为你的服务器启用这项安全特性,你需要在服务器接收到一个通过 HTTPS 发起的请求时返回一个 Public-Key-Pins HTTP 响应头:

Public-Key-Pins: pin-sha256="base64=="; max-age=expireTime [; includeSubDomains][; report-uri="reportURI"]
  • pin-sha256
    • 一个带引号的 SPKISubject Public Key Information 指纹的 Base64 字符串。不同的公钥可以被指定多个固定值。一些浏览器可能在未来允许 SHA-256 以外其他的哈希算法。如何获得证书或者公钥的这个信息请看下文。
  • max-age
    • 浏览器应该只使用这一个确定的公钥的时长。以秒作为单位。
  • includeSubDomains 可选
    • 如果这一个参数被指定了,则这一个规则适用于该站点的所有子域名。
  • report-uri 可选
    • 如果这一个参数被指定了,则公钥固定失败时会发送报告至给定的 URL。

警告

正在使用的规则中需要包含一个第二固定公钥。这样会允许服务器更换公钥而不会破坏已经固定了公钥的客户端。这对于更换公钥非常重要。

获得公钥信息的 Base64 编码

警告

虽然这一个示例展示了如何去为服务器设置一个固定值,但是还是建议将 CA 的中间证书也一起固定来缓解更新证书带来的麻烦。

首先你需要从你的证书中导出公钥的信息然后再将它用 Base64 编码。

下面的命令可以帮助你导出证书信息的 Base64 编码。

Terminal window
$ 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 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

下面的命令可以导出一个网站的信息。

Terminal window
$ 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

示例 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” 则告知了应该向何处报告固定失败。

Report-Only 响应头

除了使用 Public-Key-Pins 响应头外,你可以同时使用 Public-Key-Pins-Report-Only 响应头。这个响应头将使浏览器在公钥固定失败的时候发送一个报告到 report-uri 上。如果仅仅使用 Public-Key-Pins-Report-Only 响应头,那么浏览器只会发送报告而不会阻止加载。

为你的服务器设置 HPKP 响应头

下面的步骤取决于你的服务器类型。

警告

下面的例子使用的 max-age 为两个月并且适用于子域名。请在部署到服务器之前再三确认是否合适你的服务器。

小心

如果错误地配置了 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

加入并修改以下代码。

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

加入并修改以下代码。

<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, section 2.1: Public-Key-PinsHTTP 的公钥固定扩展

浏览器兼容性

桌面系统

特性ChromeEdgeFireFoxIEOperaSafariServo
Public-Key-Pins(yes)?35.0?(yes)??
Public-Key-Pins-Report-Only46?不支持 1?33??
  1. 参见 Bugzilla bug 1091177

手机系统

特性AndroidChrome For AndroidEdge MobileFireFox for AndroidIE MobileOpera MobileSafari Mobile
Public-Key-Pins(Yes)(Yes)?35.0?(yes)?
Public-Key-Pins-Report-Only(Yes)(Yes)?不支持?33?

参考资料

(^o^)/~ 本文已完成。