如何生成 SRI 哈希
- 已选中 SRI 模式。在 哈希 下勾选 sha256、sha384 和/或 sha512;默认勾选 sha384。
- 将 输入 设为 文件,拖入
.js或.css文件。标签类型根据文件扩展名决定。 - 或者,将 输入 设为 文本,把文件内容粘贴到 文件内容(粘贴),选择 标签(
<script>或<link rel="stylesheet">),并填写文件将来所在的 路径 或 URL。 - 复制
integrity值或整个 HTML 标签。勾选多种算法时,它们会以空格分隔,出现在同一个属性中。
拖入文件比粘贴更可靠。浏览器文本框会把 CRLF 换行转换为 LF,而末尾少一个或多一个换行符都会改变哈希;工具会对这两点给出提醒。
integrity 值是什么
SRI 值由算法名、一个短横线,以及文件确切字节的 Base64 摘要组成。假设文件 app.js 只有一行 console.log('hello');,后面跟一个换行符:
<script src="https://cdn.example.com/app.js"
integrity="sha384-eEqgsNuDqSzbarn2kIY7c4WH6zkoJrjJRQIhlYgVwRR+6Eb8tkfTItTNLlIjbE3O"
crossorigin="anonymous"></script>
如果没有末尾的换行符,同一行的哈希是 sha384-v393mDht/MNBowq0Z9UpetDvKE6u6EdCihklP1GZs66vL1YCFm1Z4Q4wJtb94rY9。浏览器下载文件后,会对收到的响应体计算哈希(在去除 gzip 或 Brotli 传输压缩之后,所以压缩不影响结果);如果没有任何列出的哈希匹配,就拒绝执行或应用它。
什么时候值得用 SRI
当你的代码由别人提供服务时,SRI 能保护你:公共 CDN、第三方组件托管、与其他团队共用的存储桶。如果那台服务器被攻破、文件被替换,浏览器会拦截被篡改的文件,而不是在你的页面上运行它。
它适合在固定 URL 下永不变化的文件,比如 library@1.2.3/dist/lib.min.js。以下情况不适合:
/latest/或@1这类会指向新版本的 URL。- 因浏览器或地区而不同的响应,比如有些 Web 字体 CSS 会根据 User-Agent 返回不同的
@font-face规则。 - 厂商有意原地更新的脚本,比如许多统计分析和在线客服的加载脚本。对这些脚本,你只能在 SRI 和厂商的更新之间二选一。
对于你自己域名上的自有文件,SRI 作用不大:能改动文件的攻击者通常也能改动 HTML。这时内容安全策略(CSP)是更好的工具;CSP nonce 生成器 和 SHA-256 转 Base64 生成的 CSP 哈希可以覆盖这方面。
CORS 和 crossorigin 属性
带 integrity 属性的跨源文件必须以 CORS 模式获取,并且服务器必须通过 Access-Control-Allow-Origin 允许它(公共 CDN 通常是 *)。crossorigin="anonymous" 请求的就是这个;生成的标签总会带上它。没有 CORS,浏览器就无法读取字节来计算哈希,于是拦截该文件,并在控制台中提示完整性或 CORS 失败。
同源文件不需要这个属性,但加上也没有坏处。
在构建中生成 SRI 哈希
文件多的时候,请在构建步骤中计算哈希:
openssl dgst -sha384 -binary app.js | openssl base64 -A
const crypto = require("crypto");
const fs = require("fs");
const integrity = "sha384-" +
crypto.createHash("sha384").update(fs.readFileSync("app.js")).digest("base64");
console.log(integrity);
对于示例文件,两者都会输出上面的 eEqgsNuD… 值。打包工具通常有插件可以帮你把属性写进 HTML,让哈希与每次构建保持同步。要核对别人给你的值,把文件拖到这里比较即可;比较是精确的,逐字符进行。
常见问题
SRI 应该用哪种算法?
通常选 SHA-384,CDN 发布的一般也是它。浏览器接受 sha256、sha384 和 sha512;列出多个时,浏览器使用其中最强的那个,忽略较弱的。
为什么加了 integrity 之后浏览器拦截了我的脚本?
要么字节和你计算哈希时的不一样,要么文件来自另一个源却没有 CORS。查看控制台消息,与服务器实际返回的文件比对,并确认响应带有 Access-Control-Allow-Origin,标签带有 crossorigin="anonymous"。
SRI 能用于会变化的文件吗,比如 "latest" 这样的 URL?
不能。哈希锁定的是一个确切的版本,任何更新都会让页面失效,直到你更新哈希。请链接带版本号的 URL,或者在发布变更期间同时列出新旧两个哈希。
如何在命令行中生成 SRI 哈希?
运行 openssl dgst -sha384 -binary app.js | openssl base64 -A,在结果前面加上 sha384-。请保留 -binary 参数;没有它,你编码的会是 hex 文本。