HTML的`integrity`属性:实现子资源完整性(SRI)的哈希校验机制

HTML integrity 属性:保障Web安全的子资源完整性(SRI)

大家好,今天我们来深入探讨一个在现代Web开发中至关重要的安全特性:HTML的integrity属性,以及它所实现的子资源完整性(Subresource Integrity,SRI)。在当今的网络环境中,Web安全威胁无处不在,攻击者可能会篡改我们依赖的第三方资源,比如CDN上的JavaScript库或CSS样式表,从而注入恶意代码,影响用户的浏览体验甚至窃取敏感信息。SRI正是为了应对这种攻击而设计的。

什么是子资源完整性(SRI)?

SRI是一种安全措施,允许浏览器验证从服务器获取的资源(如JavaScript、CSS)是否被篡改。它通过将资源的哈希值与资源的integrity属性进行比较来实现。如果哈希值不匹配,浏览器就会阻止该资源执行,从而防止恶意代码的执行。

想象一下,你正在构建一个在线银行网站。你使用了来自CDN的jQuery库来增强用户界面。如果攻击者成功入侵CDN并修改了jQuery库,他们就可以注入恶意代码来窃取用户的银行账户信息。如果没有SRI,你的用户将毫无防备。但通过使用SRI,你可以确保浏览器只加载与你预期的哈希值匹配的jQuery库版本,从而阻止攻击。

integrity 属性:语法和用法

HTML的integrity属性用于指定资源的预期哈希值。它通常与<script><link>标签一起使用,用于加载JavaScript和CSS文件。

integrity属性的语法如下:

<link rel="stylesheet" href="https://example.com/style.css" integrity="[算法]-[base64编码的哈希值]" crossorigin="anonymous">

<script src="https://example.com/script.js" integrity="[算法]-[base64编码的哈希值]" crossorigin="anonymous"></script>

其中:

  • [算法]:指定用于生成哈希值的算法,常见的有sha256sha384sha512。选择哪种算法取决于你对安全性的要求,以及浏览器对算法的支持程度。通常,sha384是一个不错的选择,因为它在安全性和性能之间取得了良好的平衡。
  • [base64编码的哈希值]:是资源内容的哈希值,使用Base64编码进行表示。
  • crossorigin="anonymous":这个属性与CORS (跨域资源共享) 相关。当从不同的域加载资源时,需要设置crossorigin属性。对于使用了SRI的资源,通常需要设置为anonymous,以便浏览器能够安全地计算资源的哈希值。

示例:

假设我们有一个名为app.js的JavaScript文件,内容如下:

console.log("Hello, world!");

为了使用SRI,我们需要计算这个文件的哈希值。可以使用各种工具来计算哈希值,例如OpenSSL。

在Linux或macOS上,你可以使用以下命令:

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

这个命令会先计算app.js文件的SHA-384哈希值,然后将其进行Base64编码。输出结果可能如下:

KuWj9U3o27h+QYl+G932B/X71+v79g9o5+m29c+75+i0479j59u7+u9/L79

然后,我们就可以在HTML中使用这个哈希值:

<script src="app.js" integrity="sha384-KuWj9U3o27h+QYl+G932B/X71+v79g9o5+m29c+75+i0479j59u7+u9/L79" crossorigin="anonymous"></script>

SRI的工作原理

当浏览器遇到带有integrity属性的<script><link>标签时,它会执行以下步骤:

  1. 下载资源: 浏览器从指定的URL下载资源。
  2. 计算哈希值: 浏览器使用integrity属性中指定的算法(例如sha384)计算下载资源的哈希值。
  3. 比较哈希值: 浏览器将计算出的哈希值与integrity属性中提供的哈希值进行比较。
  4. 验证:
    • 如果哈希值匹配,则浏览器认为资源是完整的,并允许执行或应用该资源。
    • 如果哈希值不匹配,则浏览器认为资源已被篡改,会阻止执行或应用该资源,并在控制台中报告错误。

浏览器支持

现代浏览器对SRI的支持非常好。以下是一些主要浏览器的支持情况:

浏览器 支持情况
Chrome 支持
Firefox 支持
Safari 支持
Edge 支持
Opera 支持
Internet Explorer 不支持

需要注意的是,Internet Explorer 不支持 SRI。 为了兼容旧版本的浏览器,可以考虑使用回退方案,例如使用<noscript>标签提供备用内容。

如何生成资源的哈希值

有多种方法可以生成资源的哈希值,包括:

  • OpenSSL: 这是一个强大的命令行工具,可以在各种操作系统上使用。
  • 在线SRI生成器: 有许多在线工具可以帮助你生成资源的哈希值。例如,你可以搜索 "SRI hash generator" 在线工具。
  • 构建工具: 许多现代构建工具(例如Webpack、Parcel、Rollup)都提供了生成SRI哈希值的插件或功能。

使用OpenSSL生成哈希值的示例(如上文所述):

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

使用Node.js生成哈希值的示例:

const crypto = require('crypto');
const fs = require('fs');

function generateSRIHash(filePath, algorithm = 'sha384') {
  const fileBuffer = fs.readFileSync(filePath);
  const hash = crypto.createHash(algorithm);
  hash.update(fileBuffer);
  const base64Hash = hash.digest('base64');
  return `${algorithm}-${base64Hash}`;
}

const sriHash = generateSRIHash('app.js');
console.log(sriHash);

何时使用SRI

建议在以下情况下使用SRI:

  • 加载来自CDN的资源: CDN是攻击者的常见目标,使用SRI可以确保从CDN加载的资源没有被篡改。
  • 加载来自不受信任的来源的资源: 如果你从不受信任的来源加载资源,使用SRI可以降低风险。
  • 对于安全敏感的应用程序: 对于在线银行、电子商务网站等安全敏感的应用程序,使用SRI是至关重要的。

SRI的局限性

虽然SRI是一种非常有用的安全措施,但它也有一些局限性:

  • 需要预先知道资源的哈希值: 你需要在部署资源之前计算出哈希值,并将其添加到HTML中。如果资源发生更改,你需要重新计算哈希值并更新HTML。
  • 可能会影响性能: 浏览器需要计算资源的哈希值,这可能会增加加载时间。但是,这种影响通常很小,可以忽略不计。
  • 无法防止所有类型的攻击: SRI只能防止资源被篡改,但无法防止其他类型的攻击,例如跨站脚本攻击(XSS)。

最佳实践

以下是一些使用SRI的最佳实践:

  • 使用强哈希算法: 建议使用sha384sha512等强哈希算法。
  • 始终指定crossorigin="anonymous"属性: 尤其是在从不同的域加载资源时。
  • 定期更新资源的哈希值: 当资源发生更改时,务必更新HTML中的哈希值。
  • 使用构建工具来自动生成SRI哈希值: 许多构建工具都提供了生成SRI哈希值的插件或功能,可以简化这个过程。
  • 结合其他安全措施: SRI只是一种安全措施,应该与其他安全措施(例如内容安全策略(CSP))结合使用,以提供更全面的保护。

代码示例:使用SRI加载Bootstrap CSS和JavaScript

假设我们想使用SRI从CDN加载Bootstrap CSS和JavaScript文件。

首先,我们需要找到Bootstrap CSS和JavaScript文件的URL,以及它们的SRI哈希值。这些信息通常可以在CDN提供商的网站上找到。

假设我们找到了以下信息:

  • Bootstrap CSS:
    • URL: https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css
    • SRI: sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z
  • Bootstrap JavaScript:
    • URL: https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js
    • SRI: sha384-B4gt1jrGC7Jh4AgTPSdUtOBvfO8shuf57BaghqFfPlYxofvL8/KUEfYiJOMMV+vo
  • jQuery:
    • URL: https://code.jquery.com/jquery-3.5.1.slim.min.js
    • SRI: sha384-DfXdz2htPH0lsQQjN3xnxgqvXKrjDds6uE3TcOHcWr7x9JvoRxT2MZw1T
  • Popper.js:
    • URL: https://cdn.jsdelivr.net/npm/@popperjs/[email protected]/dist/umd/popper.min.js
    • SRI: sha384-xRm+z2d4f/vK9K2f96n33c09F0j1b5t8b6+j6s2j5+z8y6l9y6l9y6l9y6l9

然后,我们可以将这些信息添加到我们的HTML中:

<!DOCTYPE html>
<html>
<head>
  <title>Bootstrap with SRI</title>
  <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">
</head>
<body>

  <h1>Hello, world!</h1>

  <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsQQjN3xnxgqvXKrjDds6uE3TcOHcWr7x9JvoRxT2MZw1T" crossorigin="anonymous"></script>
  <script src="https://cdn.jsdelivr.net/npm/@popperjs/[email protected]/dist/umd/popper.min.js" integrity="sha384-xRm+z2d4f/vK9K2f96n33c09F0j1b5t8b6+j6s2j5+z8y6l9y6l9y6l9y6l9" crossorigin="anonymous"></script>
  <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" integrity="sha384-B4gt1jrGC7Jh4AgTPSdUtOBvfO8shuf57BaghqFfPlYxofvL8/KUEfYiJOMMV+vo" crossorigin="anonymous"></script>
</body>
</html>

在这个例子中,我们使用了integrity属性来确保浏览器只加载与我们预期的哈希值匹配的Bootstrap CSS和JavaScript文件。如果攻击者篡改了这些文件,浏览器就会阻止它们执行,从而保护我们的用户。

总结

HTML的integrity属性和子资源完整性(SRI)是一个强大的安全机制,能够显著提高Web应用程序的安全性。 通过验证资源的哈希值,SRI可以防止恶意代码的注入,确保用户访问的资源是可信的。 尽管SRI有其局限性,但它仍然是现代Web开发中不可或缺的一部分,建议开发者在项目中积极使用SRI,并结合其他安全措施,以构建更安全的Web应用程序。

发表回复

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