如果你曾经使用过 CDN 托管的 JavaScript 库,你可能会注意到 script 标签上有一个奇怪的 integrity 属性。这个属性包含了看似冗长的毫无意义的字符,你可能会在清理冗余代码时想把它们去掉。

这些冗长的字符实际上是一个非常有用的安全特性,称为 Subresource Integrity(中文翻译为子资源完整性,简写为SRI),它可以帮助保护你的网站免受某些类型的黑客攻击和破坏。在本文中,我们将了解什么是SRI,它是如何帮助保护你的,以及你如何在自己的项目中使用它。

由来

由来

回想以前,JavaScript 是和 HTML 及CSS 混杂在一起的,我们无需过多考虑网站中的脚本会被用做攻击的工具。大多数网站都托管在自己的一台物理服务器上,当谈到安全最佳实践时,我们考虑的是保护该服务器

随着浏览器变得更加强大,网速更快,我们开始使用越来越多的JavaScript,最终可重用的JavaScript 库开始涌现。在早期,像 script.aculo.us、Prototype 和 jQuery 这样的库开始被普遍采用以增强网页的交互性。

随着这些库和插件的增加,页面体积也越来越大,然后我们开始认真考虑优化前端性能。像 CDN 这样的资源,以前是大公司的储备,现在对于从事 Web 开发的人来说已经司空见惯。

在这个过程中,有些聪明的开发者注意到了许多网站都在使用自己的公共库文件,例如最新的 jQuery,如果在一个公共 CDN 上托管这个文件可供每个网站使用,那样用户就不必每次都下载相同的文件了。他们在首次使用此文件时下载,然后会保存在浏览器缓存里,访问其他网站时如果再用到此文件时直接从浏览器缓存中读取即可!

这就是为什么像 jsdelivr 这类 CDN 非常受欢迎的原因,它们托管了许多流行的类库可供所有网站使用。

遇到了什么问题?

遇到了什么问题?

使用 CDN 仍然是一种有效提升前端性能的方式,不过也带了潜在的安全问题。让我们假设现在是2012年,每个人都在使用新出的 jQuery 1.8。回到传统的做事方式,每个网站都有自己的 jQuery 1.8文件,寄放在自己的服务器上以供自己的网站使用。

如果你是攻击者,并且想出了一个偷偷摸摸的方法来黑掉这个类库以获取非法所得,你必须单独针对每个网站,单独攻击每个网站的服务器。那样需要耗费巨大的精力。

但现在情况并非如此,因为每个网站都在使用从公共 CDN 加载的 jQuery。而我说的每个网站,并不是指几百个网页。我指的是数百万个网页。突然那个文件成了黑客非常有吸引力的目标。如果他们可以黑掉这一个文件,他们可以非常迅速地在全球数百万个网页上运行代码

恶意代码是什么并不重要。可以是破坏页面的恶作剧代码,可能是窃取密码的代码,可能是挖掘加密货币的代码,也可能是悄悄记录用户行为用于精准市场营销的追踪代码。重要的是,开发人员添加到页面中的文件已经被更改,现在你的网站中有一些恶意的 JavaScript 代码在运行。这是个大问题。

引入 Subresource Integrity

引入 Subresource Integrity

我们不会因噎废食就放弃使用 CDN,SRI 提供了一种简单的提升安全的方案。SRI 和 integrity 属性的作用是确保你链接到网页的文件永远不会改变。如果它真的改变了,浏览器就会拒绝执行这个文件。

检查代码是否更改是计算机科学中一个非常老的问题,有一些非常好的解决方案。SRI 采用了最简单的方法——文件散列法(file hashing)。

文件散列是接收文件并通过算法运行文件内容生成一个短字符串的过程,这个生成的字符串称之为哈希值(hash)。这个过程是可重复的,如果你给别人一个文件和它对应的哈希值,他们就可以运行相同的算法来检查两者是否匹配。如果文件改变了或者哈希值改变了,那么就不再匹配了,你知道有问题,应该不信任这个文件。

使用 SRI 时,你的网页保存哈希值,服务器(CDN或其他地方)托管文件。浏览器下载文件,然后快速计算以确保它与 integrity 属性中的哈希值匹配。如果匹配,则使用该文件,如果不匹配,则被阻止。

使用 SRI

使用 SRI

现在使用 BootstrapCDN 引入文件,官方会提供如下的引入代码:

可以看到 src 属性值指向了 CDN 上的文件,integrity 属性值就是对应的哈希值。

哈希值实际上分为两部分。第一部分是个前缀,它声明了所使用的是哪种哈希算法,上面的例子中采用的是 sha384。后面是一个横线以及使用了 base64 编码后的哈希值。

在浏览器中执行时,它会首先下载文件。在执行文件之前先 base64 解码哈希值,然后使用 sha384 哈希算法确保哈希值和刚下载的文件是匹配的。如果匹配,就会执行文件。

在浏览器中执行上述代码,可以看到 CSS 和 JS 文件都正确加载并执行了。在浏览器的开发者工具 Network 面板可以看到网络请求情况:

让我们看一下如果修改了 bootstrap.min.js 的哈希值会怎么样:

如上所见,文件被加载了,但是哈希值不再匹配,所以浏览器拒绝执行该文件。

在你的项目中使用 SRI

在你的项目中使用 SRI

CDN 为公共类库提供的这个功能很棒,但是不止于此,你也可以在自己的网站使用 SRI。你可以为自己的文件生成哈希值,然后浏览器在执行前会执行校验,就像 CDN 提供的功能那样。

有一些工具可以生成 SRI 哈希值。你可以在命令行使用 openssl :

或者在命令使用 shasum :

此外,有些网站如 https://www.srihash.org/ 可以在线生成 SRI 哈希值。

如果你的资源文件和引入的网页位于不同的域名下,浏览器还会额外使用 CORS 检查资源文件,以确保允许来自其他域名的引用。因此,在托管资源文件的服务器上必须设置 Access-Control-Allow-Origin 响应头,以允许来自其他域名的请求。例如:

最后要说的是浏览器兼容性。目前 SRI 已经在主流浏览器上得到了支持,除了 IE。你可以放心使用这个特性,由于它是向后兼容的,在不支持 SRI 的浏览器上也不会引起问题,只不过失去了额外的保护。

结语

结语

我们不仅了解了 integrity 属性中那些奇怪的散列值的作用,还学习了如何使用它们来防御对于网站的攻击。当然了,没有一劳永逸的方式可以保护我们的网站免受各种类型的攻击,但是子资源完整性是一个非常有用的工具。