HTTP公钥固定(HPKP)模拟演示 - 常见问题
HPKP(HTTP Public Key Pinning)是一种浏览器安全机制,通过HTTP响应头Public-Key-Pins告诉浏览器应该信任哪些公钥。当浏览器首次访问网站时,会记录响应头中指定的公钥SHA-256哈希值(即PIN值)。在后续访问中,浏览器会验证服务器提供的证书链中是否包含与缓存PIN匹配的公钥。如果验证失败,浏览器会中断连接并向报告地址发送违规报告。HPKP的主要目的是防止证书颁发机构(CA)被攻破或错误签发证书时导致的中间人攻击。该标准在RFC 7469中定义,但已于2018年被主流浏览器废弃。
HPKP被废弃的原因主要有以下几点:第一,实施门槛过高,需要网站管理员准确理解公钥固定的概念并正确配置,一旦配置错误(如忘记包含备份PIN),可能导致网站完全无法访问,且在max-age过期前无法恢复。第二,对于共享主机环境(如CDN)极不友好,因为不同租户可能使用不同的证书,HPKP策略无法灵活适配。第三,恶意网站可以利用HPKP进行"勒索式"攻击——攻击者获得短暂的服务器控制权后设置HPKP策略并包含自己的PIN,即使之后失去控制权,网站也会在max-age期间无法正常访问。由于这些问题,Chrome于2018年在版本72中移除了HPKP支持,转而推荐使用Expect-CT等更安全的替代方案。
计算pin-sha256的值需要以下步骤:首先,从服务器证书或中间CA证书中提取公钥的SPKI(Subject Public Key Info)信息。SPKI是X.509证书中存储公钥数据的标准结构,包含算法标识和公钥数据。然后,对SPKI结构的原始字节序列进行SHA-256哈希运算,得到一个32字节的摘要。最后,将这个32字节的摘要进行Base64编码,得到44个字符的PIN值。在本工具中,这个过程通过Web Crypto API自动完成。实际操作中,也可以使用OpenSSL命令行工具来计算:先用openssl x509 -pubkey -noout提取公钥,再用openssl pkey -pubin -outform DER获取DER编码的公钥,最后用openssl dgst -sha256 -binary | base64计算哈希。
HPKP配置中至少需要两个PIN值的原因是为了保证服务的可用性和密钥更换的灵活性。第一个PIN通常对应当前服务器证书的公钥,第二个PIN对应一个备份证书的公钥。如果没有备份PIN,当需要更换证书(如证书过期、私钥泄露、需要更换CA等)时,新证书的公钥PIN不在浏览器缓存中,所有之前访问过网站的用户都会看到安全警告,导致网站无法正常访问。备份PIN的证书应该预先生成好但不需要安装到服务器上,只需保证在需要时能够快速部署。最佳实践是维护两个或更多的备份PIN,并定期(如每年)轮换备份密钥对。这也是HPKP被废弃的原因之一——对于许多网站来说,维护多个备份密钥对的管理成本过高。
在HPKP中,可以固定不同层级证书的PIN:叶子证书(服务器证书)、中间CA证书或根CA证书。推荐的做法是固定中间CA证书的公钥PIN,原因如下:第一,服务器证书会定期更换(通常90天),每次更换都需要更新HPKP策略并等待旧的max-age过期,管理成本很高。第二,根CA证书很少变化,但直接固定根CA意味着完全信任该CA签发的所有证书,安全性不如固定中间CA。第三,中间CA证书的有效期通常较长(1-5年),且一个CA通常只有少数几个中间CA,固定中间CA的PIN既能保证安全性,又能减少密钥更换的频率。如果固定中间CA的PIN,即使服务器证书更换,只要新证书由同一个中间CA签发,HPKP验证就能通过。不过需要注意的是,如果CA的中间CA密钥泄露,固定该中间CA PIN的所有网站都会受到影响。
max-age的设置需要在安全性和灵活性之间取得平衡。建议采用渐进式部署策略:初始测试阶段设置为300秒(5分钟),确认配置无误后逐步增加到86400秒(1天),再增加到604800秒(7天),最终在生产环境中设置为较长的时间如15552000秒(180天)或更长。较长的max-age可以提供更持久的保护,但同时增加了配置错误后恢复的难度。如果在max-age过期前需要更换PIN或取消HPKP策略,必须确保旧PIN持续有效直到所有客户端缓存过期。对于使用自动证书管理(如Let's Encrypt)的网站,建议max-age不超过证书有效期的一半。此外,如果max-age设置过短(如小于60秒),某些浏览器可能会忽略该指令。
Report-Only模式和report-uri是HPKP中两个相关但不同的概念。report-uri是指定违规报告接收地址的指令,无论是在强制执行模式还是Report-Only模式中都可以使用。当浏览器检测到PIN验证失败时,会向report-uri发送JSON格式的违规报告。Report-Only模式则是一种特殊的部署方式,使用Public-Key-Pins-Report-Only响应头代替Public-Key-Pins。在Report-Only模式下,浏览器会记录PIN值并在后续访问中验证,但验证失败时不会阻断连接,仅发送违规报告。而在强制执行模式下,验证失败会直接阻断连接,可能导致用户无法访问网站。推荐在正式部署HPKP之前先使用Report-Only模式运行一段时间,收集违规数据并确认配置正确,然后再切换到强制执行模式。需要注意的是,Report-Only模式不能提供真正的安全保护,因为它不会阻止连接。
HPKP和HSTS都是通过HTTP响应头增强网站安全性的机制,但它们解决的问题不同。HSTS(HTTP Strict Transport Security)强制浏览器使用HTTPS连接,防止协议降级攻击和Cookie劫持,通过Strict-Transport-Security响应头实现。HPKP则固定服务器的公钥指纹,防止因CA被攻破或错误签发证书导致的中间人攻击。两者的主要区别在于:HSTS仍在被广泛使用且是推荐的安全实践,而HPKP已被废弃;HSTS保护的是传输层(确保使用HTTPS),HPKP保护的是身份验证层(确保使用正确的公钥);HSTS的实施相对简单,只需确保网站支持HTTPS即可,而HPKP需要管理密钥对和备份PIN,实施复杂度高。在实际项目中,应始终启用HSTS,而对于证书透明度保护,应使用Certificate Transparency和Expect-CT代替HPKP。
证书透明度(Certificate Transparency,CT)通过另一条路径解决HPKP试图解决的问题——防止错误签发的证书被用于中间人攻击。CT要求所有公共CA将签发的证书记录到公开的、仅追加的日志中,任何人都可以审计这些日志以发现异常签发。浏览器通过SCT(Signed Certificate Timestamp)机制确保证书在一定时间内被记录到CT日志中。Chrome等浏览器已经要求所有新签发的TLS证书必须包含SCT。CT的优势在于:不需要网站管理员配置公钥固定,实施零成本;CA端的透明度机制对网站完全透明;误签发可以被快速发现和撤销。但CT不能阻止错误签发的证书在被发现之前被使用,它更侧重于事后检测而非事前预防。配合Expect-CT头,网站可以要求浏览器在检测到CT违规时进行报告或阻断。
includeSubDomains指令将HPKP策略扩展到所有子域名,虽然可以提供更全面的保护,但也带来了显著的风险。主要风险包括:第一,配置复杂性增加,所有子域名都必须使用与HPKP PIN匹配的证书,如果某个子域名使用了不同的CA或证书,该子域名将无法正常访问。第二,对于使用共享主机或CDN的网站,不同子域名可能由不同的服务提供商管理,统一的HPKP策略难以实施。第三,如果某个子域名被攻破并设置了恶意的HPKP策略,该策略会影响到所有其他子域名。第四,在微服务架构中,不同服务可能使用不同的证书,includeSubDomains会导致管理困难。因此,除非所有子域名都由同一团队管理且使用统一的证书基础设施,否则不建议启用includeSubDomains。如果必须使用,建议先在不包含子域名的情况下验证HPKP配置正确,再逐步启用includeSubDomains。
UD5工具箱