沙箱隔离调研
主要设计
整体设计
- 借鉴
with
的实现效果,在 webpack 编译打包阶段为每个子应用代码包裹一层代码 或者 通过 new Function 拼接代码,创建一个闭包,传入自己模拟的 window、document、location、history 等全局对象。
- 在模拟的 Context 中,new 一个 iframe 对象,提供一个和宿主应用空的(about:blank) 同域 URL 来作为这个 iframe 初始加载的 URL(空的 URL 不会发生资源加载,但是会产生和这个 iframe 中关联的 history 不能被操作的问题,这时路由的变换只支持 hash 模式),然后将其下的原生浏览器对象通过
contentWindow
取出来(因为 iframe 对象天然隔离,这里省去了自己 Mock 实现所有 API 的成本)。
- 取出对应的 iframe 中原生的对象之后,继续对特定需要隔离的对象生成对应的 Proxy,然后对一些属性获取和属性设置,做一些特定的实现(比如 window.document 需要返回特定的沙箱 document 而不是当前浏览器的document 等)。
- 为了文档内容能够被加载在同一个 DOM 树上,对于 document,大部分的 DOM 操作的属性和方法仍旧直接使用宿主浏览器中的 document 的属性和方法处理等。
基本原理
- js沙箱原理:沙箱的目的其实就是隔离变量,代理某些变量,防止污染宿主环境. 在浏览器中,我们可以利用 js 闭包的能力,利用变量作用域去模拟一个沙箱环境,最简单的例子:这段代码的输出一定是 { createElement: "***" }, 而不是浏览器原生的 document

- 难点:如何模拟浏览器原生window? 有两个方案,一是根据 ECMA 规范实现这一堆的浏览器原生对象,显然这个成本太高.所以这里有个取巧的办法,我们可以创建一个 iframe并设置src="about:blank",这个时候可以确保 url 同域,也不会发生资源加载.然后取出 iframe 对应的 contentWindow,利用其天然隔离特性,省去了自己实现的成本.


对比其他方案
- snapshotSandbox主要用于不支持 es6 Proxy的浏览器,并且也只适合单实例子应用.
snapshotSandbox
会污染全局window,但是可以支持不兼容Proxy
的浏览器。
- proxySandbox 代理沙箱其代理对象来自普通对象 fakeWindow,然后将 window 上的 d
ocument
、location
、top
、window 等属性拷贝一份到 fakeWindow
优点是不会污染全局 window,缺点是没有 iframe.contentWindow 的隔离那么彻底.
- 基于 Web Worker,webAssembly,iframe 实现沙箱参考文章: https://cloud.tencent.com/developer/article/1887036
- 基于客户端 JavaScriptCore 实现沙箱这个需要客户端(iOS, android)配合 https://www.jianshu.com/p/a9d72a36456d
主要流程图

Reference

https://docs.qq.com/pdf/DWXB5SXhOSFVTemh3