JS `Subresource Integrity` (SRI) 保护第三方脚本完整性

admin
Mon, 21 Jul 2025 09:51:12 +0000
Javascript, integrity, js, subresource

各位观众老爷们，大家好！今天老夫就来跟大家聊聊一个能让你的网站更安全的秘密武器——Subresource Integrity (SRI)，也就是“子资源完整性”。 开场白：互联网的“信任危机” 咱们先来想象一个场景：你辛辛苦苦搭建了一个网站，用了各种炫酷的 JavaScript 库，比如 jQuery、Bootstrap、React啥的。这些库你肯定是从 CDN 上直接引用的，方便快捷嘛！但是，你有没有想过，万一 CDN 被黑客入侵了，或者 CDN 提供商自己作恶，悄悄地在这些库里加了点恶意代码，你的网站是不是就遭殃了？用户的数据是不是就泄露了？ 这可不是危言耸听，互联网上这种事情屡见不鲜。所以，我们需要一种机制来确保我们引用的第三方资源是安全可靠的，没有被篡改过。这就是 SRI 闪亮登场的时候了！ SRI：给你的代码上个“保险锁” SRI 其实很简单，就是给你的第三方资源加一个“校验码”，这个校验码就像是货物的防伪标签，能确保你拿到的东西和原始版本一模一样。如果有人试图篡改这个资源，校验码就会失效，浏览器就会拒绝加载它。 SRI 的原理：哈希算法 这个“校验码”是怎么生成的呢？答案就 …

继续阅读“JS `Subresource Integrity` (SRI) 保护第三方脚本完整性”

The post JS `Subresource Integrity` (SRI) 保护第三方脚本完整性 first appeared on 智猿学院-前后端，数据库，人工智能，云计算等领域前沿技术讲座.

各位观众老爷们，大家好！今天老夫就来跟大家聊聊一个能让你的网站更安全的秘密武器——Subresource Integrity (SRI)，也就是“子资源完整性”。

开场白：互联网的“信任危机”

咱们先来想象一个场景：你辛辛苦苦搭建了一个网站，用了各种炫酷的 JavaScript 库，比如 jQuery、Bootstrap、React啥的。这些库你肯定是从 CDN 上直接引用的，方便快捷嘛！但是，你有没有想过，万一 CDN 被黑客入侵了，或者 CDN 提供商自己作恶，悄悄地在这些库里加了点恶意代码，你的网站是不是就遭殃了？用户的数据是不是就泄露了？

这可不是危言耸听，互联网上这种事情屡见不鲜。所以，我们需要一种机制来确保我们引用的第三方资源是安全可靠的，没有被篡改过。这就是 SRI 闪亮登场的时候了！

SRI：给你的代码上个“保险锁”

SRI 其实很简单，就是给你的第三方资源加一个“校验码”，这个校验码就像是货物的防伪标签，能确保你拿到的东西和原始版本一模一样。如果有人试图篡改这个资源，校验码就会失效，浏览器就会拒绝加载它。

SRI 的原理：哈希算法

这个“校验码”是怎么生成的呢？答案就是哈希算法。哈希算法是一种单向加密算法，它可以把任意长度的数据转换成固定长度的哈希值，而且这个过程是不可逆的。也就是说，你只能通过原始数据算出哈希值，而无法通过哈希值反推出原始数据。

常用的哈希算法有很多，比如 SHA-256、SHA-384、SHA-512 等等。SRI 推荐使用 SHA-256 或更强的算法，因为 SHA-1 已经被证明存在安全漏洞。

SRI 的使用方法：很简单，三步走！

1. 生成哈希值：

首先，你需要拿到你要引用的第三方资源的原始文件。你可以从 CDN 上下载，也可以从官方网站下载。然后，使用哈希算法生成它的哈希值。

在 Linux/macOS 系统下，你可以使用 openssl 命令来生成哈希值：

openssl dgst -sha256 your-file.js
openssl dgst -sha384 your-file.js
openssl dgst -sha512 your-file.js

例如，假设你要引用一个名为 jquery-3.6.0.min.js 的 jQuery 文件，你可以这样生成它的 SHA-256 哈希值：

openssl dgst -sha256 jquery-3.6.0.min.js

你会得到类似这样的输出：

SHA256(jquery-3.6.0.min.js)= b92074c474b2285a25d185575541c68d4a0a586e30d93935504121e88630905b

这个 b92074c474b2285a25d185575541c68d4a0a586e30d93935504121e88630905b 就是 jquery-3.6.0.min.js 的 SHA-256 哈希值。

如果你的系统没有 openssl 命令，你也可以使用在线的哈希值生成工具，比如 https://srihash.org/。

2. 修改 HTML 代码：

接下来，你需要修改你的 HTML 代码，在 或 标签中添加 integrity 属性，并将哈希值填进去。

<script
  src="https://cdn.example.com/jquery-3.6.0.min.js"
  integrity="sha256-b92074c474b2285a25d185575541c68d4a0a586e30d93935504121e88630905b"
  crossorigin="anonymous"
>

<link
  rel="stylesheet"
  href="https://cdn.example.com/bootstrap.min.css"
  integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm"
  crossorigin="anonymous"
/>

integrity 属性的值由三部分组成：

哈希算法的名称 (比如 sha256, sha384, sha512)

一个短横线 (-)

Base64 编码的哈希值

crossorigin 属性：

由于 SRI 涉及到跨域资源，所以你还需要添加 crossorigin 属性，并设置其值为 anonymous。

crossorigin="anonymous" 表示以匿名模式请求资源，不发送用户的 Cookie 和 HTTP 认证信息。

如果你的 CDN 支持 CORS，并且设置了 Access-Control-Allow-Origin 响应头，你也可以将 crossorigin 设置为 use-credentials，这样浏览器会发送用户的 Cookie 和 HTTP 认证信息。但是，这需要 CDN 的配合，否则可能会导致跨域请求失败。

3. 测试：

最后，你需要测试一下 SRI 是否生效。你可以故意修改一下 CDN 上的文件，比如在文件末尾添加一个空格，然后刷新你的网页。如果 SRI 生效，浏览器会报错，并且拒绝加载这个被篡改的文件。

你可以在浏览器的开发者工具的控制台中看到类似的错误信息：

Failed to find a valid digest in the 'integrity' attribute for resource 'https://cdn.example.com/jquery-3.6.0.min.js' with computed SHA-256 integrity 'sha256-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'. The resource has been blocked.

这个错误信息告诉你，浏览器检测到 CDN 上的文件被篡改了，所以拒绝加载它。

SRI 的优点：

增强安全性： 确保第三方资源没有被篡改，防止恶意代码注入。

提高可靠性： 即使 CDN 出现故障，SRI 也能保证你的网站正常运行。因为浏览器会拒绝加载损坏的资源，你可以使用 fallback 机制加载本地备份。

提升性能： SRI 可以让浏览器更早地验证资源的完整性，从而减少加载时间。

SRI 的缺点：

需要维护： 如果 CDN 上的资源更新了，你需要重新生成哈希值，并更新你的 HTML 代码。

增加复杂度： 虽然 SRI 的使用方法很简单，但是也增加了一些开发和维护的复杂度。

兼容性问题： 较旧的浏览器可能不支持 SRI，你需要使用 polyfill 来提供兼容性。

SRI 的最佳实践：

使用可靠的 CDN： 选择信誉良好、安全措施完善的 CDN 提供商。

使用多个哈希算法： 为了提高安全性，你可以同时使用多个哈希算法，比如 SHA-256 和 SHA-384。

使用 fallback 机制： 为了防止 CDN 出现故障，你可以使用 fallback 机制加载本地备份。

<script
  src="https://cdn.example.com/jquery-3.6.0.min.js"
  integrity="sha256-b92074c474b2285a25d185575541c68d4a0a586e30d93935504121e88630905b sha384-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  crossorigin="anonymous"
>

  // Fallback mechanism
  if (typeof jQuery === 'undefined') {
    document.write(
' '
    );
  }


这段代码首先尝试从 CDN 加载 jQuery，如果加载失败，或者 jQuery 未定义，就加载本地备份。

定期更新哈希值： 定期检查 CDN 上的资源是否更新，并更新你的 HTML 代码中的哈希值。可以使用脚本自动化更新哈希值。

SRI 的兼容性：

浏览器 | 支持程度
Chrome | 支持
Firefox | 支持
Safari | 支持
Edge | 支持
Internet Explorer | 不支持

对于不支持 SRI 的浏览器，SRI 属性会被忽略，浏览器会正常加载资源。为了提供更好的兼容性，你可以使用 polyfill。

SRI 和 CSP 的关系：

SRI 和 Content Security Policy (CSP) 都是增强网站安全性的重要技术，但是它们的作用不同。

SRI： 确保第三方资源的完整性，防止资源被篡改。

CSP： 限制浏览器可以加载的资源来源，防止跨站脚本攻击 (XSS)。

你可以同时使用 SRI 和 CSP 来提高网站的安全性。例如，你可以使用 CSP 的 script-src 指令来限制脚本的来源，并使用 SRI 来确保这些脚本的完整性。

一个更复杂的例子：使用脚本自动更新 SRI 哈希值

手动更新 SRI 哈希值是一件繁琐的事情，特别是当你有大量的第三方资源需要维护时。我们可以使用脚本来自动化这个过程。

以下是一个使用 Node.js 脚本自动更新 HTML 文件中 SRI 哈希值的例子：

const fs = require('fs');
const crypto = require('crypto');
const cheerio = require('cheerio'); // 使用 cheerio 解析 HTML

// 配置文件
const config = {
  htmlFile: 'index.html', // HTML 文件路径
  resourceMap: {
    'https://cdn.example.com/jquery-3.6.0.min.js': 'js/jquery-3.6.0.min.js',
    'https://cdn.example.com/bootstrap.min.css': 'css/bootstrap.min.css',
    // 添加更多资源
  },
};

// 函数：生成 SRI 哈希值
function generateSRIHash(filePath, algorithm = 'sha256') {
  const fileContent = fs.readFileSync(filePath);
  const hash = crypto.createHash(algorithm);
  hash.update(fileContent);
  const sriHash = algorithm + '-' + hash.digest('base64');
  return sriHash;
}

// 函数：更新 HTML 文件中的 SRI 属性
function updateSRIInHTML(htmlFile, resourceMap) {
  const htmlContent = fs.readFileSync(htmlFile, 'utf-8');
  const $ = cheerio.load(htmlContent);

  for (const cdnUrl in resourceMap) {
    const localPath = resourceMap[cdnUrl];
    const sriHash = generateSRIHash(localPath);

    // 查找匹配的 script 或 link 标签
    $(`script[src="${cdnUrl}"], link[href="${cdnUrl}"]`).each((i, element) => {
      $(element).attr('integrity', sriHash);
    });
  }

  // 写回更新后的 HTML 文件
  fs.writeFileSync(htmlFile, $.html());
  console.log(`SRI hashes updated in ${htmlFile}`);
}

// 主函数
function main() {
  updateSRIInHTML(config.htmlFile, config.resourceMap);
}

main();

使用方法：

1. 安装依赖：

npm install cheerio

2. 配置脚本：

修改 config.htmlFile 为你的 HTML 文件路径。

修改 config.resourceMap 为你的 CDN 资源和本地文件路径的映射。确保本地文件是最新的。

3. 运行脚本：

node update-sri.js

(假设你的脚本文件名为 update-sri.js)

这个脚本会读取你的 HTML 文件，找到指定的 CDN 资源，生成对应的 SRI 哈希值，然后更新 HTML 文件中的 integrity 属性。

注意事项：

你需要定期运行这个脚本，以确保你的 HTML 文件中的 SRI 哈希值是最新的。

这个脚本依赖于 cheerio 库来解析 HTML 文件。你可以使用其他的 HTML 解析库，比如 jsdom。

你可以根据你的实际需求修改这个脚本，比如添加错误处理、日志记录等等。

确保你下载的本地文件与CDN的文件内容一致。

总结：

SRI 是一个简单而强大的工具，可以帮助你提高网站的安全性。虽然它有一些缺点，但是它的优点远远大于缺点。所以，如果你还没有使用 SRI，赶紧行动起来吧！别让你的网站成为黑客的乐园！

好了，今天的讲座就到这里。希望大家有所收获！感谢各位观众老爷的收听！记得点赞、关注、转发哦！下次再见！