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>
其中:
[算法]:指定用于生成哈希值的算法,常见的有sha256、sha384和sha512。选择哪种算法取决于你对安全性的要求,以及浏览器对算法的支持程度。通常,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>标签时,它会执行以下步骤:
- 下载资源: 浏览器从指定的URL下载资源。
- 计算哈希值: 浏览器使用
integrity属性中指定的算法(例如sha384)计算下载资源的哈希值。 - 比较哈希值: 浏览器将计算出的哈希值与
integrity属性中提供的哈希值进行比较。 - 验证:
- 如果哈希值匹配,则浏览器认为资源是完整的,并允许执行或应用该资源。
- 如果哈希值不匹配,则浏览器认为资源已被篡改,会阻止执行或应用该资源,并在控制台中报告错误。
浏览器支持
现代浏览器对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的最佳实践:
- 使用强哈希算法: 建议使用
sha384或sha512等强哈希算法。 - 始终指定
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
- URL:
- Bootstrap JavaScript:
- URL:
https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js - SRI:
sha384-B4gt1jrGC7Jh4AgTPSdUtOBvfO8shuf57BaghqFfPlYxofvL8/KUEfYiJOMMV+vo
- URL:
- jQuery:
- URL:
https://code.jquery.com/jquery-3.5.1.slim.min.js - SRI:
sha384-DfXdz2htPH0lsQQjN3xnxgqvXKrjDds6uE3TcOHcWr7x9JvoRxT2MZw1T
- URL:
- Popper.js:
- URL:
https://cdn.jsdelivr.net/npm/@popperjs/[email protected]/dist/umd/popper.min.js - SRI:
sha384-xRm+z2d4f/vK9K2f96n33c09F0j1b5t8b6+j6s2j5+z8y6l9y6l9y6l9y6l9
- URL:
然后,我们可以将这些信息添加到我们的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应用程序。