JS `Subresource Integrity (SRI)`:第三方脚本完整性验证

各位观众老爷,大家好!我是你们的老朋友,今天给大家带来一场关于“JS Subresource Integrity (SRI):第三方脚本完整性验证”的脱口秀式技术讲座。保证让大家在欢声笑语中掌握这项重要的安全技能,妈妈再也不用担心我的网站被篡改啦!

开场白:那个被篡改的午后…

咳咳,话说有一天,风和日丽,我开开心心地打开自己维护的网站,准备迎接新的一天。结果… 网站的背景音乐变成了《小苹果》,而且页面上还飘满了“恭喜发财,红包拿来”的弹窗!当时我的内心是崩溃的,感觉就像精心打扮去相亲,结果发现对方穿的是秋裤外穿的超人装…

后来排查发现,罪魁祸首竟然是我引入的一个第三方JS库被黑客篡改了!黑客在里面加了恶意代码,导致我的网站被挂马了。

痛定思痛,我开始研究如何防止这种事情再次发生。于是,我发现了今天的主角——Subresource Integrity (SRI)。

什么是 Subresource Integrity (SRI)?

简单来说,SRI 就像是给每个从 CDN 引入的第三方 JS 和 CSS 文件贴上一个“防伪标签”。这个标签是根据文件的内容计算出来的哈希值。当浏览器加载这些文件时,会对比实际下载到的文件内容和标签上的哈希值是否一致。如果一致,说明文件没有被篡改,浏览器才会执行;如果不一致,浏览器就会拒绝执行,从而保证网站的安全。

是不是有点像我们去超市买东西,要检查一下包装上的条形码是否正确?

SRI 的工作原理

SRI 的核心在于使用密码学哈希函数,例如 SHA-256, SHA-384, SHA-512 等。这些哈希函数可以将任意长度的数据转换成固定长度的字符串,而且只要原始数据稍有修改,哈希值就会发生巨大的变化。

  1. 生成哈希值: 首先,我们需要拿到第三方 JS 或 CSS 文件的原始版本。然后,使用 openssl 或其他工具生成文件的哈希值。

    openssl dgst -sha384 -binary your-script.js | openssl base64 -A

    这条命令会先用 SHA-384 算法计算 your-script.js 的哈希值,然后将结果进行 Base64 编码,得到最终的 SRI 哈希值。

  2. 添加到 HTML: 将生成的哈希值添加到 HTML 文件的 <script><link> 标签中,作为 integrity 属性的值。同时,还需要指定 crossorigin="anonymous" 属性,这是出于安全考虑,允许浏览器在跨域请求时获取错误信息。

    <script src="https://cdn.example.com/your-script.js"
            integrity="sha384-YOUR_HASH_VALUE"
            crossorigin="anonymous"></script>
  3. 浏览器验证: 当浏览器加载这个 <script> 标签时,会先下载 your-script.js 文件,然后计算下载到的文件的哈希值,并与 integrity 属性中的哈希值进行比较。如果两者一致,浏览器就会执行这个脚本;否则,浏览器会拒绝执行,并在控制台中报错。

SRI 的优势

  • 防止恶意篡改: 这是 SRI 最核心的优势。即使 CDN 被攻击,黑客篡改了文件,浏览器也能检测出来并阻止执行,从而保护网站的安全。
  • 提高安全性: SRI 可以与其他安全措施(例如 HTTPS)结合使用,进一步提高网站的安全性。
  • 简单易用: SRI 的配置非常简单,只需要生成哈希值并添加到 HTML 标签中即可。
  • 广泛支持: 现代浏览器都支持 SRI。

SRI 的局限性

  • 需要原始文件: SRI 需要拿到第三方 JS 或 CSS 文件的原始版本才能生成哈希值。如果无法获取原始文件,就无法使用 SRI。
  • 更新哈希值: 当第三方文件更新时,我们需要重新生成哈希值并更新 HTML 文件。这可能会增加一些维护成本。
  • 只验证文件内容: SRI 只能验证文件内容是否被篡改,无法防止其他类型的攻击,例如 XSS 攻击。

实战演练:手把手教你使用 SRI

为了让大家更好地理解 SRI 的使用方法,我们来做一个简单的实战演练。

  1. 选择一个第三方 JS 库: 我们选择常用的 jQuery 库作为示例。

  2. 获取 jQuery 的原始文件: 从 jQuery 官网下载 jQuery 的原始文件(例如 jquery-3.6.0.min.js)。

  3. 生成哈希值: 使用 openssl 或其他工具生成 jquery-3.6.0.min.js 文件的哈希值。

    openssl dgst -sha384 -binary jquery-3.6.0.min.js | openssl base64 -A

    假设生成的哈希值为:sha384-nvAa0+6Qg9clwYCGGPpDQLVdtK3G5nQn2ju5UkhH9PY6hGNevNRAklA/tkYZVP+zQI2D4wFnQ3m9uvcWq356w==

  4. 添加到 HTML 文件: 将以下代码添加到 HTML 文件的 <head> 标签中:

    <script src="https://code.jquery.com/jquery-3.6.0.min.js"
            integrity="sha384-nvAa0+6Qg9clwYCGGPpDQLVdtK3G5nQn2ju5UkhH9PY6hGNevNRAklA/tkYZVP+zQI2D4wFnQ3m9uvcWq356w=="
            crossorigin="anonymous"></script>
  5. 测试: 在浏览器中打开 HTML 文件,查看控制台。如果没有报错,说明 SRI 配置成功。

模拟攻击:验证 SRI 的有效性

为了验证 SRI 的有效性,我们可以模拟一次攻击,篡改 jquery-3.6.0.min.js 文件,例如在文件末尾添加一行 console.log("Hacked!");

然后,再次在浏览器中打开 HTML 文件,查看控制台。你会发现浏览器报错,提示 SRI 验证失败,并且拒绝执行 jQuery 脚本。

这说明 SRI 成功阻止了恶意代码的执行,保护了网站的安全。

更高级的用法:使用多个哈希值

为了提高安全性,我们可以为同一个文件指定多个哈希值。浏览器会依次尝试这些哈希值,只要有一个匹配成功,就会执行该文件。

<script src="https://cdn.example.com/your-script.js"
        integrity="sha256-HASH1 sha384-HASH2 sha512-HASH3"
        crossorigin="anonymous"></script>

这样做的好处是,即使某个哈希算法被破解,或者 CDN 上的文件被替换成了使用不同哈希算法的版本,浏览器仍然可以验证文件的完整性。

最佳实践

  • 使用可靠的 CDN: 选择信誉良好、安全性高的 CDN 服务商。
  • 定期更新哈希值: 当第三方文件更新时,及时更新 HTML 文件中的哈希值。
  • 使用多个哈希值: 为同一个文件指定多个哈希值,提高安全性。
  • 监控 SRI 错误: 监控浏览器的控制台,及时发现 SRI 错误并进行处理。

SRI 与其他安全措施的配合

SRI 只是网站安全的一部分,不能单独使用。我们需要将 SRI 与其他安全措施结合使用,才能构建一个更加安全的网站。

  • HTTPS: 使用 HTTPS 协议加密网站的流量,防止中间人攻击。
  • Content Security Policy (CSP): 使用 CSP 限制浏览器可以加载的资源,防止 XSS 攻击。
  • 定期安全扫描: 定期对网站进行安全扫描,及时发现潜在的安全漏洞。

一些常见的问题和解答

  • Q:为什么需要 crossorigin="anonymous" 属性?

    A:crossorigin="anonymous" 属性告诉浏览器,在跨域请求时,允许获取错误信息。这是 SRI 工作所必需的,因为浏览器需要获取文件的内容才能计算哈希值。

  • Q:如果 CDN 不支持 CORS,还能使用 SRI 吗?

    A:不能。SRI 需要 CDN 支持 CORS 才能正常工作。

  • Q:SRI 会影响网站的性能吗?

    A:SRI 会增加一些额外的计算开销,但通常来说,这种开销是可以忽略不计的。

表格总结

特性 优点 缺点
SRI 工作原理 使用哈希函数验证第三方资源完整性 需要原始文件,更新需重新生成哈希值
SRI 的优势 防止恶意篡改,提高安全性,简单易用,广泛支持 只验证文件内容,不能防范所有攻击
最佳实践 使用可靠 CDN,定期更新哈希值,使用多个哈希值,监控 SRI 错误
与其他安全措施配合 HTTPS,CSP,定期安全扫描

结语:安全之路,永无止境

各位观众老爷,今天的 SRI 脱口秀就到这里了。希望大家通过今天的学习,能够掌握 SRI 这项重要的安全技能,保护自己的网站免受恶意攻击。

记住,网络安全之路,永无止境。我们要不断学习新的安全技术,才能更好地保护自己的网站和用户的数据。

感谢大家的观看,我们下期再见!

(鞠躬下台)

发表回复

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