一、什么是HPKP?
HTTP公钥固定(HPKP)是一种安全功能,它告诉Web客户端将特定加密公钥与某个Web服务器相关联,以降低使用伪造证书进行“MITM攻击(中间人攻击)”的风险。
为了确保TLS会话中使用的服务器公钥的真实性,此公钥将包装到X.509证书中,该证书通常由证书颁发机构(CA)签名。诸如浏览器之类的Web客户端信任许多这些CA,它们都可以为任意域名创建证书。如果攻击者能够破坏单个CA,则他们可以对各种TLS连接执行MITM攻击。 HPKP可以通过告知客户端哪个公钥属于某个Web服务器来规避HTTPS协议的威胁。
HPKP是首次使用信任(TOFU)技术。 Web服务器第一次通过特殊的HTTP头告诉客户端哪些公钥属于它,客户端将该信息存储在给定的时间段内。当客户端再次访问服务器时,它希望证书链中至少有一个证书包含一个公钥,其指纹已通过HPKP获知。如果服务器提供未知的公钥,则客户端应向用户发出警告。
二、如何查看网站是否成功启动HPKP?
直接可以用浏览器F12调试来查看;下图我是用的火狐浏览器查看的,结果是“公钥固定:已禁用”。
二、如何启用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。 请参阅下文,了解如何从证书或密钥文件中提取此信息。
这里可以使用OpenSSL 命令来生成 SPKI 指纹。
max-age
浏览器应记住仅使用其中一个已定义的密钥访问此站点的时间(以秒为单位)。
includeSubDomains 可选
如果指定了此可选参数,则此规则也适用于所有站点的子域。
report-uri 可选
如果指定了此可选参数,则会将引脚验证失败报告给给定的URL。
首先,您需要从证书或密钥文件中提取公钥信息,并使用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”解释了报告引脚验证失败的位置。
Report-Only header
您也可以使用Public-Key-Pins-Report-Only标头,而不是使用Public-Key-Pins标头。 此标头仅将报告发送到标头中指定的report-uri,并且即使违反了固定,仍允许浏览器连接到Web服务器。
设置您的Web服务器以包含HPKP header
传递HPKP标头所需的具体步骤取决于您使用的Web服务器。下面再说说Apache、Nginx、Lighttpd、IIS分别如何设置HPKP?
Apache
在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="base64+primary=="; pin-sha256="base64+backup=="; max-age=5184000; includeSubDomains" />
</customHeaders>
</httpProtocol>
...
</system.webServer>