JS `HTTP Public Key Pinning` (HPKP) (已弃用) / `Certificate Transparency` (证书透明度)

咳咳,各位观众老爷们,晚上好!欢迎来到“网络安全那些事儿”小讲堂。今天咱们聊点稍微有点“过气网红”气质的东西,但绝对是网络安全领域里闪耀过的星星——HTTP Public Key Pinning (HPKP) 和 Certificate Transparency (证书透明)。

先别急着打哈欠,我知道HPKP已经被弃用了,但这并不妨碍我们从它的失败中学习经验。而Certificate Transparency,简称CT,虽然听起来高大上,但其实它就像一个“阳光下的秘密”,把证书颁发的过程晒出来,让坏人无处遁形。

咱们先从“爱作妖”的HPKP开始说起。

一、HPKP:理想很丰满,现实很骨感

HPKP,全称HTTP Public Key Pinning,翻译过来就是“HTTP公钥固定”。它的核心思想是:网站告诉浏览器,以后访问我的时候,只能信任特定的几个公钥。

1. HPKP的原理:

想象一下,你开了一家包子铺(你的网站),为了防止有人冒充你卖假包子,你给每位顾客发了一张“会员卡”(公钥)。这张卡上印着只有你家包子铺才有的特殊防伪标记。以后顾客来买包子,必须出示这张卡,确认身份无误才能卖给他。

这样一来,即使有人拿了假的会员卡(伪造的证书)想冒充你,浏览器(顾客)也会发现卡上的防伪标记不对,拒绝交易。

2. HPKP的配置:

HPKP通过HTTP头部来配置。服务器在响应头中添加Public-Key-Pins字段,告诉浏览器应该信任哪些公钥。

Public-Key-Pins: pin-sha256="base64-encoded-sha256-hash-of-pinned-key"; max-age=expire-time; includeSubDomains; report-uri="report-url"
  • pin-sha256: 指定要固定的公钥的SHA256哈希值。你需要把你的证书或者中间证书的公钥提取出来,计算SHA256哈希值,然后进行Base64编码。
  • max-age: 指定浏览器记住这个pin的时间,单位是秒。通常建议设置为比较长的时间,比如几个月甚至一年。
  • includeSubDomains: 可选参数,表示该pin也适用于所有子域名。
  • report-uri: 可选参数,指定一个URL,浏览器如果检测到pin验证失败,会向这个URL发送报告。

举个栗子:

假设你的网站 example.com 使用了Let’s Encrypt颁发的证书。

首先,你需要提取你的证书(或中间证书)的公钥,并计算SHA256哈希值:

openssl x509 -in example.com.crt -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64

假设运行结果是:"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="

你还需要一个备用的pin(以防万一你的证书需要更换):

openssl x509 -in example.com.crt -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64

假设运行结果是:"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB="

那么你的HPKP头看起来会是这样:

Public-Key-Pins: pin-sha256="AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; pin-sha256="BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB="; max-age=5184000; includeSubDomains; report-uri="https://example.com/report"

3. HPKP的问题:

理想很美好,现实很残酷。HPKP虽然能提高安全性,但它的“坑”也很多:

  • 配置错误导致网站瘫痪: 如果配置错了,比如你pin了错误的公钥,或者忘记了备用pin,浏览器就会拒绝访问你的网站。这就像你给顾客发了错误的会员卡,导致他们无法买到包子,包子铺直接关门大吉。
  • 证书轮换困难: 如果你的证书需要更换,你需要提前更新你的HPKP配置,否则浏览器会拒绝新的证书。这就像你要更换会员卡的防伪标记,必须提前通知所有顾客,否则他们会以为你是假包子铺。
  • 难以管理: 对于大型网站来说,管理大量的pin非常困难。
  • 容易被滥用: 恶意攻击者可能会利用HPKP来“锁定”某个网站的公钥,导致合法用户无法访问。

4. HPKP的“死亡”:

由于以上种种原因,HPKP最终被各大浏览器厂商弃用。Chrome在Chrome 67中移除了HPKP支持,Firefox也在Firefox 72中移除了HPKP支持。

5. HPKP的教训:

HPKP的失败告诉我们,安全措施不能过于复杂,否则会适得其反。同时也说明,安全措施需要考虑到易用性和可管理性。

二、Certificate Transparency (CT):阳光是最好的防腐剂

HPKP失败了,但我们不能因此放弃对HTTPS安全性的追求。Certificate Transparency (CT) 应运而生,它采用了一种更简单、更有效的方式来提高HTTPS的安全性。

1. CT的原理:

CT的核心思想是:将证书颁发的过程公开透明化。所有证书颁发机构 (CA) 颁发的证书都必须记录在公开的、可审计的日志服务器中。

这就像你开了一家银行,所有的存取款记录都必须公开记录在账本上,任何人都可以查阅。这样一来,如果有人伪造银行卡,或者非法转移资金,就会很容易被发现。

2. CT的组成部分:

  • 证书颁发机构 (CA): 负责颁发证书。
  • CT日志服务器: 负责存储证书记录。
  • 监控者 (Monitors): 负责监控CT日志,发现异常情况。
  • 审核者 (Auditors): 负责审核CT日志的完整性和一致性。

3. CT的工作流程:

  1. 网站所有者向CA申请证书。
  2. CA颁发证书后,将证书信息提交给CT日志服务器。
  3. CT日志服务器将证书信息添加到日志中,并返回一个Signed Certificate Timestamp (SCT)。SCT相当于一个“收据”,证明证书已经被记录在日志中。
  4. CA将SCT嵌入到证书中,或者通过其他方式(比如OCSP Stapling)提供给浏览器。
  5. 浏览器在验证证书时,会检查SCT是否有效。如果SCT有效,则认为证书是可信的。
  6. 监控者和审核者会定期检查CT日志,发现异常情况。

4. SCT的传递方式:

SCT可以通过以下三种方式传递给浏览器:

  • X.509v3 Extension: SCT直接嵌入到证书的扩展字段中。
  • TLS Extension: SCT通过TLS握手协议传递。
  • OCSP Stapling: SCT通过OCSP Stapling传递。

5. CT的好处:

  • 提高安全性: CT可以有效地防止CA颁发未经授权的证书,降低中间人攻击的风险。
  • 增强透明度: CT将证书颁发的过程公开透明化,让所有人都可以监督CA的行为。
  • 促进信任: CT可以增强用户对HTTPS的信任,促进HTTPS的普及。

6. CT的实施:

各大浏览器厂商都要求CA必须支持CT。Chrome从Chrome 69开始,要求所有新颁发的证书都必须符合CT的要求。

7. 如何查看证书的CT信息:

你可以通过Chrome浏览器的开发者工具查看证书的CT信息。

  1. 打开Chrome浏览器,访问一个HTTPS网站。
  2. 按下F12键,打开开发者工具。
  3. 选择 "Security" 选项卡。
  4. 点击 "View certificate" 按钮。
  5. 在证书详情中,你可以找到 "Certificate Transparency" 相关的 section,查看 SCT 信息。

8. CT的代码示例:

虽然你不能直接用JS代码来“实现”CT(因为CT主要由CA和浏览器实现),但你可以使用JS来验证证书的CT信息。

这里提供一个简单的例子,使用Node.js和node-forge库来解析证书并提取SCT信息:

const forge = require('node-forge');
const fs = require('fs');

// 读取证书文件
const certPem = fs.readFileSync('example.com.crt', 'utf8');

// 解析证书
const cert = forge.pki.certificateFromPem(certPem);

// 获取证书扩展
const extensions = cert.extensions;

// 查找 CT 扩展 (OID: 1.3.6.1.4.1.11129.2.4.2)
const ctExtension = extensions.find(ext => ext.id === '1.3.6.1.4.1.11129.2.4.2');

if (ctExtension) {
  // 解析 Signed Certificate Timestamp List
  const sctListAsn1 = forge.asn1.fromDer(ctExtension.value);

  // 遍历 SCT List
  sctListAsn1.value.forEach(sctAsn1 => {
    // 解析单个 SCT
    const sct = {
      version: sctAsn1.value[0].value,
      logId: forge.util.bytesToHex(sctAsn1.value[1].value),
      timestamp: sctAsn1.value[2].value.toNumber(),
      extensions: forge.util.bytesToHex(sctAsn1.value[3].value),
      signature: {
        algorithm: {
          algorithm: forge.pki.oids[sctAsn1.value[4].value[0].value],
        },
        signature: forge.util.bytesToHex(sctAsn1.value[4].value[1].value),
      },
    };

    console.log('SCT:', sct);
  });
} else {
  console.log('No Certificate Transparency information found in the certificate.');
}

注意:

  • 你需要安装 node-forge 库: npm install node-forge
  • 你需要将 example.com.crt 替换成你的证书文件路径。
  • 这个代码只是一个简单的示例,用于演示如何提取SCT信息。实际应用中,你需要对SCT进行更严格的验证。

9. CT的未来:

CT已经成为HTTPS安全性的重要组成部分。未来,CT将会更加普及,并发挥更大的作用。

三、HPKP vs CT:殊途同归

HPKP和CT的目标都是提高HTTPS的安全性,但它们采用的方式却截然不同。

特性 HPKP CT
原理 公钥固定 证书透明化
实施者 网站管理员 证书颁发机构 (CA)、浏览器
风险 配置错误导致网站瘫痪 对CA的依赖性较高
易用性 复杂 相对简单
可管理性
适用场景 理论上适用于所有HTTPS网站 适用于所有HTTPS网站
现状 已弃用 强制要求
核心思想 信任特定的公钥 信任透明的证书颁发过程

HPKP过于复杂和脆弱,最终走向了失败。CT则更加简单、有效,并得到了广泛的应用。

四、总结:

虽然HPKP已经成为了历史,但它给我们留下了深刻的教训。在设计安全措施时,我们不仅要考虑安全性,还要考虑易用性和可管理性。CT的成功则告诉我们,透明化是提高安全性的有效手段。

希望今天的讲座能让你对HPKP和CT有一个更深入的了解。记住,网络安全之路,任重道远,需要我们不断学习和探索。

下课!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注