管中窥豹各种沙盒技术
What
沙箱,即 sandbox,顾名思义,就是让你的程序跑在一个隔离的环境下,不对外界的其他程序造成影响,通过创建类似沙盒的独立作业环境,在其内部运行的程序并不能对硬盘产生永久性的影响。举个简单的栗子,其实我们的浏览器,Chrome 中的每一个标签页都是一个沙箱(sandbox)。渲染进程被沙箱(sandbox)隔离,网页 web 代码内容必须通过 IPC 通道才能与浏览器内核进程通信,通信过程会进行安全的检查。沙箱设计的目的是为了让不可信的代码运行在一定的环境中,从而限制这些代码访问隔离区之外的资源。
— 摘自:说说JS中的沙箱
一言以蔽之,沙盒(sandbox)就是程序运行时的隔离环境,以此屏蔽程序运行时对外界的一切副作用。其实我们已经在工作中接触到了各种沙盒技术:
- docker :大名鼎鼎又耳熟能详的容器技术方案,服务端的微服务主要就是通过 docker 技术实现虚拟化的底层支持,使服务开发者可以体会不到环境的区别、抹平运行时差异的。可以说对微服务来说,docker 是这些年能得到如此的发展的一个基石。
- 操作系统进程:程序是指令、数据及其组织形式的描述,进程是程序的实体,进程间相互隔离,通过 IPC 机制进行通信。上述 chrome tab 其实就是一个进程啦。
- Node.js VM:Node.js 默认提供的一个内建模块,
VM
模块提供了一系列 API 用于在 V8 虚拟机环境中编译和运行代码。JavaScript 代码可以被编译并立即运行,或编译、保存然后再运行。
1 | const vm = require('vm'); |
- iframe:一种古老的沙盒了,方便、简单、但不怎么安全,在线代码编辑 https://codesandbox.io/s/news 就使用了 iframe 方式创建隔离环境。
1 | <iframe sandbox src="..." /> |
Why
- 引入第三方脚本(开源、ISV 入驻)如果不做隔离会引起安全问题,如 app crash、资损。
- 持续🔥的“微前端”中沙盒环境保证运行时的 app 隔离和 css 隔离。
How
with + new Function + proxy
eval 和 new Function 都被用来动态执行一段脚本,两者的区别在于
eval
是全局对象的一个函数属性,执行的代码拥有着和应用中其它正常代码一样的的权限,它能访问「执行上下文」中的局部变量,也能访问所有「全局变量」- Function 构造器生成的函数,并不会在创建它的上下文中创建闭包,一般在全局作用域中被创建。当运行函数的时候,只能访问自己的本地变量和全局变量,不能访问 Function 构造器被调用生成的上下文的作用域。
1 | function compileCode (src) { |
无论 eval
还是 function
,执行时都会把作用域一层一层向上查找,如果找不到会一直到 global
那么就可以利用 ES6 特性 Proxy
的原理就是,让执行了代码在 sandobx
中找的到,以达到「防逃逸」的目的。
1 | function evalute(code,sandbox) { |
上述方案在大部分场景已经够用了,但这依然不是一个绝对安全的沙盒,留个课后作业,试试看如何突破这个 sandbox 。
**
**
生产环境下的实践
1 | // 获取异步模块 |
阿里云微前端方案沙箱
- wepback 的插件在应用代码构建的时候给子应用代码加上一层 wrap 代码,创建一个闭包,把需要隔离的浏览器原生对象变成从下面函数闭包中获取的,从而我们可以在应用加载的时候,传入模拟掉的 window,document 之类的对象。
1 | // 打包代码 |
- 通过 new iframe 对象,把里面的原生浏览器对象通过 contentWindow 取出来,实现这一堆浏览器的原生对象。
阿里云开放平台微前端方案的沙箱实现:https://developer.aliyun.com/article/761446