HTTP公钥固定(Public Key Pinning)扩展:HPKP 详解 2025-05-20 网络安全 暂无评论 139 次阅读 #一、什么是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 已禁用 #三、如何启用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标头: ``` ... ... ``` 转自https://www.fujieace.com/http/public-key-pinning.html 标签: http, HPKP 本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。