前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【干货】加强 web 静态资源安全方法之SRI

【干货】加强 web 静态资源安全方法之SRI

作者头像
腾讯NEXT学位
发布2018-12-04 13:54:42
10.7K0
发布2018-12-04 13:54:42
举报
文章被收录于专栏:腾讯NEXT学位

我们通常会用CSP加强站点JS资源的执行限制,有效降低XSS攻击;我们通过HTTPS链接加密资源,减少站点资源劫持风险等等大量的前端安全方案。但你可能还没听说 Subresource Integrity (SRI) 子资源完整性校验。

本文将带你了解SRI是什么,能解决哪些安全风险,如何快速接入。同时,使用它又会带来哪些问题,以及浏览器的支持情况如何。

什么是SRI ?

SRI 是 Subresource Integrity 的缩写,可翻译为:子资源完整性,它也是由 Web 应用安全工作组(Web Application Security Working Group)发布。

截止2018年11月13日,已经进入建议阶段。提议地址:http://www.w3.org/TR/SRI/

通过对 HTML 中外链 JS/CSS 进行摘要签名,保证浏览器下载的资源的完整性,防止资源被篡改。

解决什么问题?

Web 性能优化中为了让静态资源尽快下载完,通常我们将 JS/CSS/Image 等静态资源部署在 CDN 服务器。CDN 服务提供商通过分布在各地的节点,让用户从最近的节点下载资源,大幅提升下载速度。但是 CDN 的安全性一直是一个风险点,让请求从第三方服务器经过,由第三方响应,安全性不可控,一旦 CDN 出现安全问题,就导致我们的站点也出现安全风险。

我们知道 CSP(Content Security Policy) 的机制可以降低 XSS 风险。但我们存储在 CDN 的内容被篡改而导致的 XSS,CSP 并不能防范,因为网站所使用的 CDN 域名,肯定在 CSP 白名单之中。因此 SRI 就应运而生了,通过它避免用户加载了第三方服务器被修改的资源。

使用方法

使用 SRI 之前你的 html 引用 JS 方式如下:

代码语言:javascript
复制
<script src="https://7.url/fudao/pc/vender-xxx.js" crossorigin="anonymous"></script>

使用 SRI 之后,变成这样了:

代码语言:javascript
复制
<script src="https://7.url/fudao/pc/vender-xxx.js"
        integrity="sha384-Li9vy3DqF8tnTXuiaAJuML3ky+er10rcgNR/VqsVpcw+ThHmYcwiB1pbOxEbzJr7"
        crossorigin="anonymous"></script>

使用前后其实就多了一个属性 integrity,integrity 值分成两个部分,第一部分指定哈希值的生成算法(目前支持 sha256、sha384 及 sha512),第二部分是经过 base64 编码的实际哈希值,两者之间通过一个短横(-)分割。

启用 SRI 策略后,浏览器会对资源进行 CORS 校验,这就要求被请求的资源必须同域,或配置 Access-Control-Allow-Origin 响应头和添加crossorigin="anonymous" 这个属性。

浏览器根据以下步骤处理 SRI:

1. 当浏览器在 <script>或者 <link> 标签中遇到 integrity 属性之后,会在执行 JS或者应用 style 之前,对比所加载文件的哈希值和期望的哈希值。

2. JS 或者 style 的哈希值和期望的不一致时,浏览器必须拒绝执行 JS 或者应用style,并且会触发 error 事件返回一个网络错误。

如何快速接入?

如果你使用了 webpack 构建你的项目,推荐你使用 webpack-subresource-integrity插件(https://www.npmjs.com/package/webpack-subresource-integrity)自动地处理SRI。

⚠️上面的例子只是 JS ,SRI 也支持 CSS 安全校验

CSP 及 SRI 联合使用

你可以根据内容安全策略来配置你的服务器使得指定类型的文件遵守 SRI。这是通过在 CSP 头部添加 require-sri-for 指令实现的:

Content-Security-Policy: require-sri-for script;

这条指令规定了所有 JavaScript 都要有 integrity 属性,且通过验证才能被加载。

你也可以指定所有样式表也要通过 SRI 验证:

Content-Security-Policy: require-sri-for style;

你也可以对两者都加上验证。

启用 SRI 后会出现什么问题?怎么解决?

显而易见,当资源验证不通过,也就是用户下载的资源被劫持了,就会导致用户直接不可用,因为浏览器会触发错事件,并且丢弃下载的资源。这可能导致整个页面都不可用了!

那么这种情况怎么处理?

针对 CDN 资源失败的情况,我们可以通过添加额外的部署站点重试,例如:直接让用户从主域名下载资源,具体实现方式如下:

同步JS资源: 失败后我们直接使用 document.write 继续加载主域资源,这里可也通过构建实现。

代码语言:javascript
复制
<script src="https://7.url/fudao/pc/vender-xxx.js"
        integrity="sha384-Li9vy3DqF8tnTXuiaAJuML3ky+er10rcgNR/VqsVpcw+ThHmYcwiB1pbOxEbzJr7"
        crossorigin="anonymous"></script>
<script>
    if(!window.IMWEB_SRI["vender-xxx"]){
    // 上报
      document.write(
      '<script 
          src="https://fudao.qq.com/pc/vender-xxx.js" 
          integrity="sha384-Li9vy3DqF8tnTXuiaAJuML3ky+er10rcgNR/VqsVpcw+ThHmYcwiB1pbOxEbzJr7" 
          crossorigin="anonymous" 
          onerror="report()"
          onerror="report()" />'
      );
     }
</script>

上面的代码你可能一些疑问?️,为什么做if(!window.IMWEB_SRI["vender-xxx"])判断后直接加载主域名资源?

实际上我们在构建阶段做了处理,在每个 JS 文件里面中插入了一段代码:

代码语言:javascript
复制
window.IMWEB_SRI=window.IMWEB_SRI||{};
// 当资源加载成功后,全局就有这个变量,我们就能够判断是否需要重试主域资源
window.IMWEB_SRI["vender-xxx"]=true;

异步 JS 资源:针对站点的异步加载资源就比较容易控制了, 可以有如下实现(基于 webpack4 实现):

代码语言:javascript
复制
function scriptLoader(src) {
  return new Promise((resolve, reject) => {
    const script = document.createElement('script');
    script.onerror = reject;
    script.onload = resolve;
    script.crossorigin = 'anonymous';
    script.src = src;
    document.body.appendChild(script);
  })
}

import('xxx.js').catch((error)=>{
    // 上报重试
    const src = "//fudao.qq.com/pc/"+ error.request.split('/').pop();
    return scriptLoader(src)
    .catch(err=>{
        //重试失败
        return  Promise.reject(err);
    })
})

同步 link:直接在每个 link 上面添加异常捕获,得到 error 事件后,重新加载主域名资源,具体实现可以参考

代码语言:javascript
复制
<script>
function styleRetry() {
  // 上报重试
  var linkTag = document.createElement('link');
  linkTag.rel = 'stylesheet';
  linkTag.type = 'text/css';
  linkTag.onload = resolve;
  linkTag.onerror = function(event) {
    // 上报重试失败
  };
  linkTag.href ="//fudao.qq.com/pc/"+this.href.split('/').pop();
  var head = document.getElementsByTagName('head')[0];
  head.appendChild(linkTag);
};
</script>
<link 
    href="//7.url.cn/fudao/pc/vendor_xxx.css" 
    rel="stylesheet"
    onerror="styleRetry()"
/>

异步加载的 CSS:处理方式和 JS 加一样即可。

现状

github 很早以前都启用了 SRI 策略了。

看这里:https://githubengineering.com/subresource-integrity/

你可以直接打开 github 网站查看源代码,就能好发现其踪迹。

caniuse (https://caniuse.com/#search=sri)

从上图看出,主流浏览器基本都支持了。小伙伴赶紧加入你的项目中去吧。

原文作者:腾讯高级工程师刘华 来源:腾讯内部KM论坛

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2018-11-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 腾讯NEXT学院 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 什么是SRI ?
  • 解决什么问题?
  • 使用方法
  • CSP 及 SRI 联合使用
  • 现状
相关产品与服务
内容分发网络 CDN
内容分发网络(Content Delivery Network,CDN)通过将站点内容发布至遍布全球的海量加速节点,使其用户可就近获取所需内容,避免因网络拥堵、跨运营商、跨地域、跨境等因素带来的网络不稳定、访问延迟高等问题,有效提升下载速度、降低响应时间,提供流畅的用户体验。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档