10

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

 3 years ago
source link: https://github.com/joeyguo/blog/issues/26
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

TODO

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

劫持

流量劫持在 Web 项目中是一个老生常谈的话题了,常见的劫持方式是往 JS 代码文件中注入一段脚本,从而实现一段广告“完美”植入。

Zv22a2J.jpg!mobile

而当注入的位置稍有偏差,导致代码执行异常,页面将完全不可用。

nYZriu6.jpg!mobile

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

原本以为就此安稳一生,直到有一天,钟声再次响起,有用户反馈遇到了广告、还有打开白屏。通过具体的定位分析,最终发现返回的 CDN 文件只剩一半内容。难道 HTTPS 协议被破译了吗?其实并不是。HTTPS 是可以有效应对流量劫持的问题,然而很多提供 HTTPS 的 CDN 服务在回源的时候采用的 HTTP 协议,流量劫持便有机可乘,那么开启全链路的 HTTPS 是否就万无一失了呢?大部分情况确实如此。但如果遇到 CDN 服务入侵、源头污染,或者用户信任了异常证书导致的中间人劫持,千里之堤,溃于蚁穴,防御之门被摧毁后,便是任人宰割。为了尽可能的安全,或许我们可以再加一道防线,那就是 SRI。

SRI(Subresource Integrity)

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

使用 SRI

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

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 和最终正常加载的 URL,抽样对比两份内容是否存在差异,如存在差异,则存在内容被篡改,属于 SRI 触发的加载失败。

最终再搭配上报和告警机制,当遇到劫持问题时,及时获知。

“此处可以加上告警的图”

知道是遇到劫持问题之后,又该如何处理呢?向运营商客服投诉或工信部投诉或许是个办法。不过在此之前我们也可以先主动触发刷新 CDN 节点缓存的资源,避免被劫持污染的资源被继续访问。而对于已经缓存到异常资源的用户,特别是在不方便强制刷新页面的环境下,下次访问会先接着访问异常缓存,对此,修改文件 hash、重新发布,强制刷新资源倒也是一种解决方式。

总结

SRI 能够保证加载的资源内容是完整性,但也并非银弹,理论上如果连同主页面被劫持,去掉资源的信息摘要也就失去了 SRI 的保护机制,好在实际上还没遇到过这种情况。当然,安全之路本身就充满着荆棘与挑战,多加一层防弹衣,增加安全指数总归不会错。

资源内容完整(不被篡改)加载下来了,但加载的是否都是我们需要的资源呢?对此,我们可以启动 CSP(Content Security Policy)机制来保证加载的是需要的资源文件、执行的是正常的脚本。一方面通过制定 CSP 的外链白名单机制,限制了不可信域名的资源加载,另一方面通过开启 nonce 模式,确保执行的是正常的内联脚本。具体方式可以查看[ 《XSS终结者-CSP理论与实践》《Csp Nonce - 守护你的 inline Script》 这两篇文章。

以上为本文所有内容,


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK