内容哈希有助于保护从 CDN 获取的资源

信息安全 哈希 混合内容
2021-08-12 23:12:41

DMZ的一次对话,有人建议使用 SHA256 哈希来检查从 CDN 传递的内容在执行之前是否没有更改,类似于 Kim Dotcom 的 MEGA 最近尝试使用 CBC-MAC 所做的事情。

该机制将在浏览器级别实现,其中内容哈希将嵌入到内容的链接中。例如:

<script src="http://example.cdn/jq/jquery-1.2.3.js" hash="sha256:kMufczNYKx9B2A7x7eICQVu18YDzEMqUe3G+h5QSifw=" />

哈希将作为站点代码的一部分提供,因此只有与哈希匹配的内容才会执行。这将保护用户免受 CDN 被入侵的情况。如果在混合内容模式下运行,除了 CDN 之外的所有内容都在 SSL 上运行,它还将提供一种提供最低安全性的方法。

这种方法有什么缺陷吗?在实施过程中是否有需要考虑的重要案例?

3个回答

更新:MDN上有更多关于子资源完整性的信息 ,(截至 2016 年 12 月 12 日)显示 Chrome 45+ 和 FireFox(Gecko) 支持 43+

更新:有一个名为Subresource Integrity的 w3c 草案描述了这样的功能。

它已经在 Chromium 中实现

例如:

<script src="file.js" integrity="ni://sha256;BpfBw7ivV8q2jLiT13…"></script>

基本方法是健全的 IMO,但有一些细节需要注意:

  • 您应该在单个标签上支持多个哈希。浏览器不需要验证所有这些,验证一个抗冲突哈希就足够了。

  • 能够指定大小似乎有助于避免某种 DoS,因为您的网站需要大量资源

  • 除非您使用树形哈希,否则您无法验证不完整的文件。对于 100kB 的 javascript 文件,这不是问题,但对于 5 GB 的视频却是这样。因此,稍后应添加对树哈希的支持。

  • 我会使用匹配NI - Naming Things with Hashes 的算法标识符,并使用 urlsafe Base64 而不使用填充

  • 我将 SHA-256 指定为每个浏览器都应支持的标准算法,但允许浏览器添加其他算法。SHA-256 是:

    • 128 位级别的抗碰撞
    • NIST 标准,实现广泛可用(与 SHA-3 不同)
    • 性能不是很好,但仍然足够快以跟上移动设备上的典型网络速度。

    IMO SHA-256 是默认/强制算法的理想选择。

  • 您应该支持所有嵌入式资源、CSS、图像、视频等,而不仅仅是脚本

  • 可以考虑改用 NI url,但我更喜欢这里的基于属性的方法。属性更加灵活,不需要目标主机的配合来实现。NI 只能为每个 url 指定一个哈希值。

  • 您可以对通过 http 获取的安全散列内容禁用混合内容警告

  • 这是查看缓存是否仍然有效的好方法。当且仅当哈希匹配时它才有效。无需重新检查、日期等。如果您从不同的 url 下载资源,这也有效。例如,如果您的缓存中已经有来自 google 的 jquery,则不需要从另一个 url 重新加载它,因为相同的哈希保证[假设抗碰撞]它们将是相同的。

  • 可能存在一些与验证 http 标头相关的问题,因为这些问题会影响资源的解释。例如,mime 类型和字符集/编码就是这样的标头。

因此,一个示例可能如下所示:

<script src="http://example.cdn/jq/jquery-1.2.3.js"
     hash="sha-256:UyaQV-Ev4rdLoHyJJWCi11OHfrYv9E1aGQAlMO2X_-Q; size:103457;
           other-hash: abc..." />

为了补充@CodesInChaos 的优点:有一个更旧的机制来支持签名的 Javascript这来自于 Netscape 4 的时代,它仍然被记录在案,但不清楚 Firefox 是否仍然支持它。Internet Explorer 从不支持它,尽管微软的人玩弄了这个想法该系统搭载了来自 Java 世界的 Jar 文件格式。

你的方法看起来很容易实现;并且可以直接在 Javascript 中完成(用于散列的 Javascript 性能的数量级大约为 1 MB/s,这对于脚本来说应该足够了)。

需要注意的系统的一个缺点是:如果您修改脚本,则必须更改所有使用显式哈希引用它的页面;这在大型站点中可能非常不方便(准备好搜索和替换超过 10000 个静态文件?)。这就是签名可以提供更多灵活性的地方。

要解决的最重要问题是如何处理哈希不匹配。在这种情况下,您必须不接受内容,否则您将失去所有潜在的安全收益。但是哈希不匹配可能有相当无辜的原因,例如以新的有线格式/编码提供资源(想想无损压缩器重写 PNG 图像以使用不同的表示形式获得相同的像素,以实现稍微更好的 gzip 压缩)。或者对您的 Javascript 进行不同的字符编码或换行处理,不会恶意更改它们,但会为文件提供新的哈希值......

对我来说,解决方案是找到不太可能改变的来源,例如您自己的 CDN,或那些使用显式版本控制而不是提供可变自动更新的公共 CDN,如jsdelivrcdnjs.com 。使用这些来源,接下来通过编码备用位置的回退来增加可靠性。

我知道有两种实现,不是在浏览器本身中,而是作为具有基于 sha256 完整性验证的资源加载器的 Javascript 实现:

  1. 验证JS
  2. 需要js

它们还都提供备用源 URL 的回退,考虑到哈希验证引入了额外的故障模式,哈希不匹配,这似乎是明智的。

免责声明:我是needjs 的作者,目前还不推荐将其用于生产环境。我还没有考虑将它用于 Javascript 和 CSS 以外的资源。