joeyguo / blog

joeyguo's blog 请 Watch 或 Star
https://github.com/joeyguo/blog
1.29k stars 107 forks source link

提高资源的安全性 - SRI 与 CSP #26

Open joeyguo opened 3 years ago

joeyguo commented 3 years ago

原文地址

《前端资源加载失败优化》文章中,我们聊到了前端资源加载失败的监控方式,以及资源加载失败时的优化方案。通过对加载失败的资源更换域名动态重新加载、同时确保最终代码正常的执行顺序,从而有效地减少了因为资源加载失败导致的网页异常。到此,资源文件成功加载了!但加载到的是否就是正确的资源呢?是否会在加载过程中被半路劫持?此时又该如何监控?是否还能做更多的防护措施呢?本文将逐步进行分析。

流量劫持

流量劫持在 Web 项目中是一个老生常谈的话题了,常见的劫持方式是往 JS 代码文件中注入一段脚本,从而实现一段广告“完美”植入,而当注入的位置稍有偏差,导致代码执行异常,页面将完全不可用。

e (1)

HTTPS

上面现象在使用以明文传输、不带加密的 HTTP 协议中经常遇到,毕竟流量在传输过程中裸奔,劫持轻而易举。HTTPS 应运而生,通过证书加密等方式保证了传输过程中的数据完整性,开启 HTTPS 后,这类劫持问题基本不复存在了。

原本以为就此安稳一生,直到有一天,钟声再次响起,有用户反馈访问页面时看到了广告、还有的打开页面后白屏。通过具体的定位分析,最终发现返回的 CDN 文件只剩一半内容。

难道 HTTPS 协议被破译了吗?其实并不是。

HTTPS 是可以有效应对流量劫持的问题,然而很多提供 HTTPS 的 CDN 服务在回源的时候采用的 HTTP 协议,流量劫持便有机可乘,那么开启全链路的 HTTPS 是否就万无一失了呢?大部分情况确实如此。但如果遇到 CDN 服务入侵、源头污染,或者用户信任了异常证书导致的中间人劫持,千里之堤,溃于蚁穴,防御之门被摧毁后,依然任人宰割。为了尽可能的安全,或许我们可以再加一道防线,那就是 SRI。

SRI(Subresource Integrity)

SRI 是用来校验资源是否完整的安全方案。通过为页面引用的资源指定信息摘要,当资源被劫持篡改内容时,浏览器校验信息摘要不匹配,将会拒绝代码执行并抛出加载异常,保证加载资源的完整性。

启用 SRI

使用 SRI 只需要给页面标签添加 integrity 属性,属性值为签名算法(sha256、sha384、sha512)和摘要签名内容组成,中间用 - 分隔。

sri

function getIntegrity() {
    const hashFuncName = 'sha256';
    const hash = crypto
        .createHash(hashFuncName)
        .update(source, 'utf8')
        .digest('base64');
    return hashFuncName + '-' + hash;
}

我们也可以借助 webpack-subresource-integrity 轻易实现 integrity 的添加过程,保持对开发者透明。

开启 SRI,浏览器会对相关资源进行 CORS 校验,所以被加载的资源要么在同域下,要么得满足 CORS 机制(具体方式可查看 脚本错误量极致优化-监控上报与Script error跨源资源共享机制( CORS ) 章节)。

至此,当资源内容被劫持篡改时,浏览器校验签名不匹配将使得异常资源不被执行,并触发加载失败。从而进入到资源加载失败的监控流程中,最终可以通过切换 CDN 域名或主域名进行加载重试,直到加载上正确资源,避免资源被劫持篡改内容后注入广告或白屏等情况。

监控劫持

监控及重加载的具体方式可见 《前端资源加载失败优化》。加载失败的原因有很多,那么该如何区分哪些是由 SRI 机制触发的呢?可以采用下面思路:

  1. 当加载失败时,切换域名重加载到正确资源;
  2. 分别请求加载失败的和最终正常的 URL,抽样对比两份内容是否存在差异,存在则说明内容被篡改,属于 SRI 机制触发。

最终搭配上报和告警机制,当遇到劫持问题时,及时收到消息。

应对劫持

sri-monitor

上面是我们遇到的劫持问题,上报量急剧上升,处理后快速下降。遇到劫持问题之后,我们该如何应对呢?

向运营商客服投诉或工信部投诉或许是个办法。不过在此之前我们可以先主动触发刷新 CDN 节点缓存的资源,避免被劫持的资源被继续访问。除此之外,对于已经缓存到异常资源的用户,特别是在不方便强制刷新页面的环境下,下次访问会先接着访问异常缓存造成二次伤害,对此通过修改文件 hash,重新发布强制刷新资源,问题得以解决。

CSP(Content Security Policy)

资源内容完整(不被篡改)加载下来了,但加载的是否就都是我们需要的资源呢?是否会因为代码问题导致 XSS、或是浏览器使用了异常插件,最终导致加载了其他不需要的资源而影响业务呢?

对此,我们可以开启 CSP 机制来保证加载的是需要的资源文件、执行的是正常的脚本。

一方面通过制定 CSP 的外链白名单机制,限制了不可信域名的资源加载;另一方面通过开启 nonce 模式,确保执行的是正常的内联脚本。相关内容可以查看[《XSS终结者-CSP理论与实践》《Csp Nonce - 守护你的 inline Script》这两篇文章。

总结

HTTPS 可以有效应对流量劫持的问题,SRI 在资源完整性再上一道屏障,CSP 也进行了其他方面的补充。“三驾马车”为页面资源安全“保驾护航”。当然这也绝非银弹,安全之路充满着荆棘与挑战,任重道远。

以上为本文所有内容,如有不妥,恳请斧正,谢谢。

查看更多文章 >> https://github.com/joeyguo/blog