JS `Subresource Integrity (SRI)` 原理：防止第三方脚本篡改

admin
Wed, 16 Jul 2025 18:08:19 +0000
Javascript, integrity, js, subresource

咳咳，各位观众老爷，早上好/下午好/晚上好！ 今天咱们来聊聊一个前端安全领域的小可爱，但作用却无比巨大的家伙——Subresource Integrity，简称SRI。 别看名字高大上，其实它就是用来保护你网站上引用的那些第三方脚本，防止它们被坏人篡改，从而保证你网站的安全。 一、啥是Subresource Integrity (SRI)？ 简单来说，SRI就像是给每个你引用的第三方脚本文件都盖了个防伪戳。 你在引用的时候，除了指定脚本的URL，还要提供这个脚本的“指纹”（也就是哈希值）。浏览器在加载脚本的时候，会先检查脚本的“指纹”是不是和你提供的“防伪戳”对得上。如果对不上，说明这个脚本可能被篡改了，浏览器就会拒绝执行它，从而保护你的网站免受恶意代码的侵害。 二、为什么需要SRI？ 想象一下，你辛辛苦苦搭建了一个网站，一切都棒棒哒。 但是，你的网站用了一些第三方提供的JavaScript库，比如jQuery、Bootstrap啥的。 这些库放在CDN上，方便快捷，还能加速你的网站。 但是，如果CDN被黑了，或者被恶意人员控制了，他们就可以修改这些JavaScript库的内容，插入恶 …

继续阅读“JS `Subresource Integrity (SRI)` 原理：防止第三方脚本篡改”

The post JS `Subresource Integrity (SRI)` 原理：防止第三方脚本篡改 first appeared on 智猿学院-前后端，数据库，人工智能，云计算等领域前沿技术讲座.

咳咳，各位观众老爷，早上好/下午好/晚上好！ 今天咱们来聊聊一个前端安全领域的小可爱，但作用却无比巨大的家伙——Subresource Integrity，简称SRI。 别看名字高大上，其实它就是用来保护你网站上引用的那些第三方脚本，防止它们被坏人篡改，从而保证你网站的安全。

一、啥是Subresource Integrity (SRI)？

简单来说，SRI就像是给每个你引用的第三方脚本文件都盖了个防伪戳。 你在引用的时候，除了指定脚本的URL，还要提供这个脚本的“指纹”（也就是哈希值）。浏览器在加载脚本的时候，会先检查脚本的“指纹”是不是和你提供的“防伪戳”对得上。如果对不上，说明这个脚本可能被篡改了，浏览器就会拒绝执行它，从而保护你的网站免受恶意代码的侵害。

二、为什么需要SRI？

想象一下，你辛辛苦苦搭建了一个网站，一切都棒棒哒。 但是，你的网站用了一些第三方提供的JavaScript库，比如jQuery、Bootstrap啥的。 这些库放在CDN上，方便快捷，还能加速你的网站。 但是，如果CDN被黑了，或者被恶意人员控制了，他们就可以修改这些JavaScript库的内容，插入恶意代码。

如果你的网站没有使用SRI，那么浏览器就会直接加载这些被篡改的脚本，你的网站就可能被劫持，用户的隐私数据可能被窃取，甚至整个网站都被搞垮。 这可不是闹着玩的！

有了SRI，情况就不一样了。 即使CDN被黑了，恶意人员修改了JavaScript库的内容，浏览器在加载的时候会发现脚本的“指纹”和预期的不一致，就会拒绝执行这个脚本。 这样，你的网站就能避免被恶意代码侵害。

三、SRI的工作原理

SRI的核心就是使用哈希算法，对脚本文件进行计算，生成一个唯一的哈希值。 这个哈希值就像是脚本的身份证，可以用来验证脚本的完整性。

1. 生成哈希值： 使用SHA-256、SHA-384或SHA-512等哈希算法，对脚本文件进行计算，生成哈希值。

2. 添加到 标签： 将生成的哈希值添加到 标签的integrity属性中。

3. 浏览器验证： 浏览器在加载脚本时，会先计算脚本文件的哈希值，然后与integrity属性中提供的哈希值进行比较。 如果两个哈希值一致，说明脚本文件没有被篡改，浏览器会正常执行它。 如果两个哈希值不一致，说明脚本文件可能被篡改了，浏览器会拒绝执行它。

四、如何使用SRI？

使用SRI非常简单，只需要以下几个步骤：

1. 获取脚本文件的哈希值： 可以使用各种工具来生成脚本文件的哈希值，比如OpenSSL、Subresource Integrity Hash Generator等。

使用OpenSSL：
在命令行中执行以下命令：

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

将your-script.js替换成你的脚本文件名。

这个命令会先使用SHA-384算法计算脚本文件的哈希值，然后将哈希值进行Base64编码，最后输出结果。

在线工具：
网上有很多在线的Subresource Integrity Hash Generator工具，你只需要将脚本文件的内容复制到工具中，就可以生成哈希值。

2. 将哈希值添加到 标签： 将生成的哈希值添加到 标签的integrity属性中，并指定哈希算法。

<script
  src="https://cdn.example.com/your-script.js"
  integrity="sha384-YOUR_HASH_VALUE"
  crossorigin="anonymous"
>

将https://cdn.example.com/your-script.js替换成你的脚本URL，将sha384-YOUR_HASH_VALUE替换成你生成的哈希值。

注意：

integrity属性的值必须以哈希算法名称开头，比如sha256-、sha384-、sha512-。

crossorigin属性是必须的，它的值必须设置为anonymous或use-credentials。

anonymous：表示不发送任何身份验证信息，比如Cookie。

use-credentials：表示发送身份验证信息，比如Cookie。 只有在你的CDN服务器允许跨域请求时，才能使用use-credentials。 一般情况下，使用anonymous就可以了。

五、SRI的最佳实践

1. 使用多种哈希算法： 为了提高安全性，可以使用多种哈希算法，比如SHA-256、SHA-384和SHA-512。 这样，即使其中一种哈希算法被破解了，其他的哈希算法仍然可以保护你的网站。

<script
  src="https://cdn.example.com/your-script.js"
  integrity="sha256-HASH_SHA256 sha384-HASH_SHA384 sha512-HASH_SHA512"
  crossorigin="anonymous"
>

2. 定期更新哈希值： 如果你使用的第三方脚本更新了，那么你需要重新生成哈希值，并更新 标签中的integrity属性。

3. 监控SRI错误： 浏览器会在控制台中输出SRI错误，你可以监控这些错误，及时发现问题。

4. 备份方案： 如果SRI验证失败，浏览器会拒绝执行脚本。 为了保证网站的正常运行，你可以提供一个备份方案，比如加载本地的脚本文件。

<script
  src="https://cdn.example.com/your-script.js"
  integrity="sha384-YOUR_HASH_VALUE"
  crossorigin="anonymous"
>

  // 如果SRI验证失败，会执行这里的代码
  var script = document.createElement('script');
  script.src = 'local-script.js';
  document.head.appendChild(script);


5. 使用工具自动化SRI： 手动生成和更新哈希值非常麻烦，可以使用一些工具来自动化这个过程，比如grunt-sri、webpack-subresource-integrity等。

六、代码示例

假设我们有一个名为app.js的脚本文件，内容如下：

console.log('Hello, SRI!');

1. 生成哈希值：

使用OpenSSL：

openssl dgst -sha384 -binary app.js | openssl base64 -A

假设生成的哈希值为：i7wzYkQ/Jz4U5v5b7Z9B4n+Wv7J8N6x2Y6z7A8B9C0=

2. 添加到 标签：




SRI Example


Hello, World!
    <script
      src="app.js"
      integrity="sha384-i7wzYkQ/Jz4U5v5b7Z9B4n+Wv7J8N6x2Y6z7A8B9C0="
      crossorigin="anonymous"
>



在这个例子中，我们将app.js放在了本地服务器上，并指定了它的哈希值。

七、SRI的优点和缺点

优点：

提高安全性： 防止第三方脚本被篡改，保护网站免受恶意代码侵害。

简单易用： 只需要添加一个integrity属性就可以使用SRI。

浏览器原生支持： 不需要安装任何插件或库。

缺点：

增加维护成本： 需要定期更新哈希值。

可能导致性能问题： 浏览器需要计算哈希值，可能会稍微增加加载时间。

兼容性问题： 虽然现代浏览器都支持SRI，但是一些老旧的浏览器可能不支持。

八、SRI与CSP（Content Security Policy）

SRI和CSP都是用来提高网站安全性的技术，但是它们的作用不同。

SRI： 用来验证第三方脚本的完整性，防止脚本被篡改。

CSP： 用来限制浏览器可以加载哪些资源，防止XSS攻击。

可以将SRI和CSP结合使用，以提高网站的整体安全性。

九、总结

SRI是一个简单而强大的安全特性，可以有效地保护你的网站免受恶意代码的侵害。 虽然它有一些缺点，但是总体来说，使用SRI是值得的。 尤其是在你使用了大量的第三方脚本的情况下，SRI可以为你提供额外的安全保障。

十、彩蛋：SRI 的一些不常见的用法

SRI for CSS: 是的，你没听错，SRI 也可以用于CSS文件！ 和JS一样，你可以计算CSS文件的哈希值，然后添加到 标签中。

<link
  rel="stylesheet"
  href="style.css"
  integrity="sha384-ANOTHER_HASH_VALUE"
  crossorigin="anonymous"
/>

动态生成 SRI： 在一些高级场景下，你可能需要在运行时动态生成SRI。 例如，你可能需要根据用户的配置来加载不同的脚本，而这些脚本的哈希值需要在运行时才能确定。 这需要一些JavaScript技巧，但绝对可行。

// 假设 scriptContent 是你动态获取的脚本内容
function generateSRIHash(scriptContent) {
  const encoder = new TextEncoder();
  const data = encoder.encode(scriptContent);
  return crypto.subtle.digest('SHA-384', data)
    .then(buffer => {
      const hashArray = Array.from(new Uint8Array(buffer));
      const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
      return 'sha384-' + btoa(String.fromCharCode.apply(null, new Uint8Array(buffer))); // 转换成 base64
    });
}

// 使用示例
fetch('dynamic-script.js') // 假设从服务器获取动态脚本
  .then(response => response.text())
  .then(scriptContent => {
    generateSRIHash(scriptContent)
      .then(sriHash => {
        const script = document.createElement('script');
        script.src = 'dynamic-script.js';
        script.integrity = sriHash;
        script.crossOrigin = 'anonymous';
        document.head.appendChild(script);
      });
  });

注意: 这段代码需要在支持 crypto.subtle API 的环境中运行（比如现代浏览器）。 而且，动态生成SRI通常需要仔细考虑安全风险，确保你的代码没有引入新的漏洞。

SRI与模块化: 如果你使用模块化工具（例如Webpack、Parcel）来构建你的前端应用，这些工具通常会提供SRI的插件或配置选项，可以自动生成和管理SRI哈希值。 这大大简化了SRI的使用过程。

总而言之，SRI是一个非常实用的安全特性，可以帮助你保护你的网站免受恶意攻击。 虽然它不是万能的，但它可以为你提供额外的安全保障，让你更加安心。

好了，今天的讲座就到这里。 感谢各位的观看，希望对大家有所帮助！ 如果有什么问题，欢迎随时提问。 下次再见！