什么是SRI
子资源完整性 Subresource Integrity 简称 SRI 是一种安全机制。是允许浏览器检查其获得的资源(例如从 CDN 获得的)是否被篡改的一项安全特性。它通过验证获取文件的哈希值是否和你提供的哈希值一样来判断资源是否被篡改。
在一些CDN网站提供了带SRI的,比如 vue - Libraries - cdnjs ,可以看到如下引用:
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/3.2.47/vue.cjs.js"
integrity="sha512-DD31SaA8/u+cn6fBAdPBcABgOkmiogzhPd/dNHvkMmazQLEjiaT9ZP51rkGTpFcx+jFl0EeNCxgxLaNniutt2g==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
使用子资源完整性功能的方法是,在任何 <script>
或 <link>
元素的 integrity
属性值中,指定你要告诉浏览器所获取的资源(或文件)的 base64 编码的加密哈希值。
integrity
值至少由一个字符串开始,每个字符串包括一个前缀,表示一个特定的哈希算法(目前允许的前缀是 sha256
、sha384
和 sha512
),后面是一个短横线(-),最后是实际的 base64 编码的哈希。
可以看到引入css也支持:
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.2.3/css/bootstrap-grid.min.css"
integrity="sha512-JQksK36WdRekVrvdxNyV3B0Q1huqbTkIQNbz1dlcFVgNynEMRl0F8OSqOGdVppLUDIvsOejhr/W5L3G/b3J+8w==" crossorigin="anonymous" referrerpolicy="no-referrer" />
错误恢复
- 当浏览器遇到一个带有
integrity
的 script 或 style 标签,在执行其中的 JS 脚本或应用其中的 CSS 样式之前,浏览器会首先计算所下载文件的内容的哈希值是否与integrity
属性给定的值相同。 - 如果计算结果与给定值不匹配,浏览器会拒绝执行脚本内容,并报出一个网络错误,类似如下结果:
Failed to find a valid digest in the ‘integrity’ attribute for resource ‘https://cdnjs.cloudflare.com/ajax/libs/normalize/6.0.0/normalize.min.css’ with computed SHA-256 integrity ‘VbcxqgMGQYm3q8qZMd63uETHXXZkqs7ME1bEvAY1xK8=’. The resource has been blocked.
js的判断加载失败从本站加载:
<script src="https://code.jquery.com/jquery-3.2.1.min.js"
integrity="sha384-xBuQ/xzmlsLoJpyjoggmTEz8OWUFM0/RC5BsqQBDX2v5cMvDHcMakNTNrHIW2I5f"
crossorigin="anonymous"></script>
<script>if (!window.jQuery) document.write('<script src="/jquery-3.2.1.min.js"><\/script>')</script>
这其实是一种的服务降级方案,但是这个方案不算完整,比如css就不太容易判断和做降级。
不过一般出现加载失败,除了引用三方站点资源发生了篡改,还有可能是三方服务或网络问题导致不可用。可以js重定向到一个本站页面实现降级服务。
if(!window.jQuery) location= 'redirect_url'
CDN加载失败还有可能是被网络劫持了,还有种方式应该可以实现多个CDN或三方备用源切换,通过js加载资源方式。这样还能打点上报故障区域,为故障诊断网站运维提供信息。
var s = document.createElement('script');
s.crossOrigin = 'anonymous';
s.integrity="sha384-xxxxx";
s.onerror = function() {
// 打点上报劫持数据 (这里是用img src上报)
var img = document.createElement('img');
img.src = 'http://xxx.com?url='+s.src;
document.body.appendChild(img);
// 更换其他域名下载地址
s.src = 'http://zeptojs.com/zepto.min.js';
};
s.onload = function() {
console.log($);
}
s.src = 'http://lib.baomitu.com/zepto/1.2.0/zepto.min.js';
document.head.appendChild(s);
CSP与SRI
你可以使用 内容安全政策 (CSP)强制要求当前页面所有脚本加载标签启用 SRI。例如
Content-Security-Policy: require-sri-for script;
强制要求所有 script 标签启用 SRI,浏览器会拒绝加载未启用 SRI 的 script 标签。
对应的还有 CSS 版本:
Content-Security-Policy: require-sri-for style;
你也可以同时启用两者。
参考: