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

各位观众老爷们,大家好!今天老夫就来跟大家聊聊一个能让你的网站更安全的秘密武器——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 代码,在 <script><link> 标签中添加 integrity 属性,并将哈希值填进去。

    <script
      src="https://cdn.example.com/jquery-3.6.0.min.js"
      integrity="sha256-b92074c474b2285a25d185575541c68d4a0a586e30d93935504121e88630905b"
      crossorigin="anonymous"
    ></script>
    
    <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"
    ></script>
    <script>
      // Fallback mechanism
      if (typeof jQuery === 'undefined') {
        document.write(
          '<script src="/js/jquery-3.6.0.min.js"></script>'
        );
      }
    </script>

    这段代码首先尝试从 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,赶紧行动起来吧!别让你的网站成为黑客的乐园!

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

发表回复

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