❗ 本文最后更新于 2325 天前,文中所描述的信息可能已发生改变,请谨慎使用。
上篇文章中,我介绍了由 Google 推动的 Certificate Transparency 技术,它旨在通过开放的审计和监控系统,提高 HTTPS 网站证书安全性。本文要介绍的 HTTP Public Key Pinning(HPKP),也是用来防范由「伪造或不正当手段获得网站证书」造成的中间人攻击,但有着与 CT 不同的思路。
我们知道,受信任的 CA(证书颁发机构)有好几百个,他们成为整个网站身份认证过程中一个较大的攻击面。现有的证书信任链机制最大的问题是,任何一家受信任的 CA 都可以签发任意网站的站点证书,这些证书在浏览器看来,都是合法的。前面提到的 CT 技术能改善这种情况,但 CT 还没有普及,现阶段浏览器不能贸然阻断没有提供 SCT 信息的 HTTPS 网站。
HPKP 技术给予我们主动选择信任 CA 的权利。它的工作原理是通过响应头或者 <meta>
标签告诉浏览器当前网站的证书指纹,以及过期时间等其它信息。未来一段时间内,浏览器再次访问这个网站必须验证证书链中的证书指纹,如果跟之前指定的值不匹配,即便证书本身是合法的,也必须断开连接。
HPKP 官方文档见 RFC7469,目前 Firefox 35+ 和 Chrome 38+ 已经支持。它的基本格式如下:
Public-Key-Pins: pin-sha256="base64=="; max-age=expireTime [; includeSubdomains][; report-uri="reportURI"]
各字段含义如下:
pin-sha256
即证书指纹,允许出现多次(实际上最少应该指定两个);max-age
和includeSubdomains
分别是过期时间和是否包含子域,它们在 HSTS(HTTP Strict Transport Security)中也有,格式和含义一致;report-uri
用来指定验证失败时的上报地址,格式和含义跟 CSP(Content Security Policy)中的同名字段一致;includeSubdomains
和report-uri
两个参数均为可选;
pin-sha256
应该如何指定?显然,为了验证合法性,服务端给出的 pin-sha256
必须由网站当前证书链中的证书生成。例如我的网站证书有三级,用根证书、中间证书、站点证书中的任何一个生成指纹都可以。用站点证书生成指纹的好处是安全性最高,缺点是证书重签之后指纹就变了,如果之前没提供备用指纹,老用户无法访问;用根证书生成指纹安全性最差,因为每个根证书都对应很多中间证书,攻击者只要攻破其中一个,就可以签出能被 HPKP 策略信任的站点证书。
综合考虑安全性和易用性,一般推荐使用中间证书生成指纹;用知名 CA 的根证书也可以;不推荐使用站点证书,除非充分了解后果并且指定了有效的备用指纹。
备用指纹是为那些必须更换中间或者根证书的场景准备的 —— 例如原来的 CA 突然倒闭或者被黑了。本站当前使用的证书由 RapidSSL SHA256 CA - G4 签发,我的 pin-sha256
包含了它的指纹;同时我还用 Let's Encrypt Authority X1 这个中间证书生成了备用指纹。这样如果后续本站改为使用 Let's Encrypt Authority X1 签发的证书,老用户也不会受影响。
使用 openssl 生成证书 Public Key 指纹很简单,下面简单介绍下。
建议先验证证书是否有误:
openssl x509 -in intermediate.pem -noout -subject
subject= /C=US/O=GeoTrust Inc./CN=RapidSSL SHA256 CA - G4
通过 CN(Common Name)字段可以确认这就是本站的中间证书。接着,生成 Public Key:
openssl x509 -in intermediate.pem -noout -pubkey | openssl asn1parse -noout -inform pem -out public.key
生成指纹:
openssl dgst -sha256 -binary public.key | openssl enc -base64
aef6IF2UF6jNEwA2pNmP7kpgT6NFSdt7Tqf5HzaIGWI=
按照同样方法,生成其它中间证书的备用指纹。最后修改 Nginx 配置并重新加载即可。以下是本站的 HPKP 配置:
add_header Public-Key-Pins 'pin-sha256="aef6IF2UF6jNEwA2pNmP7kpgT6NFSdt7Tqf5HzaIGWI="; pin-sha256="YLh1dUR9y6Kja30RrAn7JKnbQG/uEtLMkBgFF2Fuihg="; max-age=2592000; includeSubDomains';
一切妥当后,可以通过 SSL Labs 这个服务来验证 HPKP 是否正常:
也可以在 Chrome 中访问 chrome://net-internals/#hsts
页面,查看指定域名的 HPKP 信息:
也许有人要问,如果用户第一次访问就被劫持怎么办?
跟 HSTS 一样,HPKP 依赖于服务端响应头,对于用户首次访问就被劫持这种情况无能为力。这个问题也只能通过浏览器内置 Preload List 来解决,现阶段 Chrome 已经内置了自家域名和各大知名网站的 HPKP 信息。不同于 HSTS Preload List,目前我没有找到个人网站申请加入 HPKP Preload List 的入口,如果你知道怎么做,不妨留言告诉我:)
更新:Chrome 69 将移除对 HPKP 的支持,详情请点击查看。
本文链接:https://imququ.com/post/http-public-key-pinning.html,参与评论 »
--EOF--
发表于 2016-03-05 18:00:12,并被添加「HTTPS、HPKP、Web安全」标签,最后修改于 2018-06-01 15:35:06。查看本文 Markdown 版本 »
专题「HTTP 相关」的其他文章 »
- 记一次图片访问异常排查过程 (Apr 28, 2023)
- HTTP Alternative Services 介绍 (Aug 21, 2016)
- 关于启用 HTTPS 的一些经验分享(三) (May 05, 2016)
- 如何压缩 HTTP 请求正文 (Apr 18, 2016)
- HTTP 协议中的 Content-Encoding (Apr 17, 2016)
- 三种解密 HTTPS 流量的方法介绍 (Mar 28, 2016)
- 关于启用 HTTPS 的一些经验分享(二) (Dec 22, 2015)
- 关于启用 HTTPS 的一些经验分享(一) (Dec 04, 2015)
- HTTP 代理原理及实现(二) (Nov 20, 2015)
- HTTP 代理原理及实现(一) (Nov 20, 2015)
Comments
Waline 评论加载中...