Subresource Integrity:不是加了就生效的安全开关

开门见山,先说核心结论:许多开发者误以为为资源添加 integrity 属性就等同于开启了自动安全防护。然而现实情况是,如果遗漏配置 crossorigin 属性、使用本地文件计算哈希值,或者CDN返回了非标准响应体,SRI校验都可能静默失效——最糟糕的情况是,页面出现白屏,而浏览器控制台甚至可能没有任何明确的错误提示。
为什么添加了 integrity 属性,却不报错也不拦截资源?
最常见的原因,是缺少了关键的 crossorigin 属性。这里有一个核心机制:浏览器一旦检测到 integrity 属性,就会强制对该资源发起CORS(跨源资源共享)请求。但如果资源服务器没有返回正确的 Access-Control-Allow-Origin 响应头,或者开发者没有显式声明 crossorigin 属性,浏览器就无法获取完整的响应体进行比对,哈希校验流程根本不会启动,integrity 属性也就被直接忽略了。
- 黄金搭档:
integrity和crossorigin必须同时存在,缺一不可。 crossorigin="anonymous":适用于绝大多数公开CDN(例如 cdn.jsdelivr.net、code.jquery.com)。crossorigin="use-credentials":仅当你的后端要求携带登录态(如Cookie)访问静态资源时才使用,并且后端必须配合返回Access-Control-Allow-Credentials: true以及明确的Access-Control-Allow-Origin(不能使用通配符*)。- 静默的陷阱:控制台通常不会友好地提示“crossorigin 属性缺失”,它只会安静地跳过校验,让你误以为一切正常,从而埋下安全隐患。
哈希值从哪里获取?切勿使用本地文件进行计算
正确的SRI哈希值必须基于目标URL实际返回的响应体内容来计算。这与你本地下载的.js文件或HTML源码内联的内容无关。CDN可能返回经过gzip压缩的内容、带有BOM头的UTF-8编码文件、或者经过重定向后的最终资源——这些细微的差异都会导致计算出的哈希值与实际不符。
- 推荐命令:
curl -sL https://cdn.example.com/jquery.min.js | openssl dgst -sha384 -binary | openssl base64 -A - 命令解析:
-sL参数确保curl跟随重定向并静默输出;openssl默认对解压后的明文内容进行哈希计算(这与浏览器的行为保持一致)。 - 在线生成器(如
https://www.srihash.org/)本质上也是调用curl并计算哈希,但无法精确控制重定向和编码细节,一旦出现问题,排查起来会更加困难。 - 如果资源服务器启用了Brotli压缩,
curl命令默认不会处理,但浏览器校验的始终是解压后的明文。因此,上述命令依然适用。
哪些HTML标签支持SRI?哪些不支持?
当前的SRI规范仅作用于 和 这两类标签。其他所有资源加载方式都不受SRI保护。
立即学习“前端免费学习笔记(深入)”;
fetch()、XMLHttpRequest、import()动态导入:完全不识别integrity属性。- 通过
document.createElement('script')动态创建并设置src:即使手动添加integrity属性,浏览器也不会执行校验。 、、:均不支持,SRI规范尚未覆盖这些元素。- 校验失败时的表现:当SRI校验失败时,对应的脚本或样式表会被浏览器直接丢弃,且不会触发
onerror事件。控制台通常只会报一个模糊的错误:Failed to find a valid digest in the 'integrity' attribute,这时你需要逐个检查所有带有integrity属性的标签。
还有一个极易被忽略的细节:当页面存在多个带有 integrity 属性的 标签时,只要其中任何一个校验失败,对应的脚本就会彻底“消失”,并且没有自动的fallback(回退)机制。因此,在将代码部署到线上生产环境之前,务必使用真实的资源URL配合 curl 命令来验证哈希值,不要依赖本地文件或缓存副本的计算结果。
