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="base64+primary=="; pin-sha256="base64+backup=="; 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日