likes
comments
collection
share

30分钟搞懂JS沙箱隔离什么是沙箱环境 在计算机安全中,沙箱(Sandbox)是一种用于隔离正在运行程序的安全机制,通常

作者站长头像
站长
· 阅读数 39

什么是沙箱环境

在计算机安全中,沙箱(Sandbox)是一种用于隔离正在运行程序的安全机制,通常用于执行未经测试或不受信任的程序或代码,它会为待执行的程序创建一个独立的执行环境,内部程序的执行不会影响到外部程序的运行。

其实在前端世界里,沙箱环境无处不在!

例如以下几个场景:

从0开始实现一个JS沙箱环境

1. 最简陋的沙箱(eval)

30分钟搞懂JS沙箱隔离什么是沙箱环境 在计算机安全中,沙箱(Sandbox)是一种用于隔离正在运行程序的安全机制,通常

问题:

  • 要求源程序在获取任意变量时都要加上执行上下文对象的前缀
  • eval的性能问题
  • 源程序可以访问闭包作用域变量
  • 源程序可以访问全局变量

2. eval + with

30分钟搞懂JS沙箱隔离什么是沙箱环境 在计算机安全中,沙箱(Sandbox)是一种用于隔离正在运行程序的安全机制,通常 问题:

  • eval的性能问题
  • 源程序可以访问闭包作用域变量
  • 源程序可以访问全局变量

3. new Function + with

30分钟搞懂JS沙箱隔离什么是沙箱环境 在计算机安全中,沙箱(Sandbox)是一种用于隔离正在运行程序的安全机制,通常 问题:

  • 源程序可以访问全局变量

4. ES6 Proxy

我们先看Proxy的使用

30分钟搞懂JS沙箱隔离什么是沙箱环境 在计算机安全中,沙箱(Sandbox)是一种用于隔离正在运行程序的安全机制,通常 Proxy{} 设置了属性访问拦截器,倘若访问的属性为 a 则返回 1,否则走正常程序。 Proxy 支持的拦截操作,一共 13 种:

  • get(target, propKey, receiver) :拦截对象属性的读取,比如proxy.fooproxy['foo']
  • set(target, propKey, value, receiver) :拦截对象属性的设置,比如proxy.foo = vproxy['foo'] = v,返回一个布尔值。
  • has(target, propKey) :拦截propKey in proxy的操作,返回一个布尔值。
  • deleteProperty(target, propKey) :拦截delete proxy[propKey]的操作,返回一个布尔值。
  • ownKeys(target) :拦截Object.getOwnPropertyNames(proxy)Object.getOwnPropertySymbols(proxy)Object.keys(proxy)for...in循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而Object.keys()的返回结果仅包括目标对象自身的可遍历属性。
  • getOwnPropertyDescriptor(target, propKey) :拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象。
  • defineProperty(target, propKey, propDesc) :拦截Object.defineProperty(proxy, propKey, propDesc)Object.defineProperties(proxy, propDescs),返回一个布尔值。
  • preventExtensions(target) :拦截Object.preventExtensions(proxy),返回一个布尔值。
  • getPrototypeOf(target) :拦截Object.getPrototypeOf(proxy),返回一个对象。
  • isExtensible(target) :拦截Object.isExtensible(proxy),返回一个布尔值。
  • setPrototypeOf(target, proto) :拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截。
  • apply(target, object, args) :拦截 Proxy 实例作为函数调用的操作,比如proxy(...args)proxy.call(object, ...args)proxy.apply(...)
  • construct(target, args) :拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(...args)

在沙箱环境中,对本身不存在的变量会追溯到全局变量上访问,此时我们可以使用 Proxy "欺骗" 程序,告诉它这个「不存在的变量」是存在的。

30分钟搞懂JS沙箱隔离什么是沙箱环境 在计算机安全中,沙箱(Sandbox)是一种用于隔离正在运行程序的安全机制,通常 报错了,因为我们阻止了所有全局变量的访问。

30分钟搞懂JS沙箱隔离什么是沙箱环境 在计算机安全中,沙箱(Sandbox)是一种用于隔离正在运行程序的安全机制,通常 继续改造:

30分钟搞懂JS沙箱隔离什么是沙箱环境 在计算机安全中,沙箱(Sandbox)是一种用于隔离正在运行程序的安全机制,通常 Symbol.unscopables

Symbol 是 JS 的第七种数据类型,它能够产生一个唯一的值,同时也具备一些内建属性,这些属性可以用来进行元编程(meta programming),即对语言本身编程,影响语言行为。其中一个内建属性 Symbol.unscopables,通过它可以影响 with 的行为,从而造成沙箱逃逸。

30分钟搞懂JS沙箱隔离什么是沙箱环境 在计算机安全中,沙箱(Sandbox)是一种用于隔离正在运行程序的安全机制,通常 对这种情况做一层加固,防止沙箱逃逸

30分钟搞懂JS沙箱隔离什么是沙箱环境 在计算机安全中,沙箱(Sandbox)是一种用于隔离正在运行程序的安全机制,通常 到这一步,其实很多较为简单的场景就可以覆盖了(比如: Vue 的模板字符串)。

仍然有很多漏洞:

  • code 中可以提前关闭 sandboxwith 语境,如 '} alert(this); {'
  • code 中可以使用 evalnew Function 直接逃逸
  • code 中可以通过访问原型链实现逃逸
  • 更为复杂的场景,如何实现任意使用诸如 documentlocation 等全局变量且不会影响主页面。

5. iframe是天然的优质沙箱

iframe 标签可以创造一个独立的浏览器原生级别的运行环境,这个环境由浏览器实现了与主环境的隔离。在 iframe 中运行的脚本程序访问到的全局对象均是当前 iframe 执行上下文提供的,不会影响其父页面的主体功能,因此使用 iframe 来实现一个沙箱是目前最方便、简单、安全的方法。

如果只考虑浏览器环境,可以用 With + Proxy + iframe 构建出一个比较好的沙箱:

  • 利用 iframe 对全局对象的天然隔离性,将 iframe.contentWindow 取出作为当前沙箱执行的全局对象
  • 将上述沙箱全局对象作为 with 的参数限制内部执行程序的访问,同时使用 Proxy 监听程序内部的访问。
  • 维护一个共享状态列表,列出需要与外部共享的全局状态,在 Proxy 内部实现访问控制。

30分钟搞懂JS沙箱隔离什么是沙箱环境 在计算机安全中,沙箱(Sandbox)是一种用于隔离正在运行程序的安全机制,通常

6. 基于ShadowRealm 提案的实现

ShadowRealm API 是一个新的 JavaScript 提案,它允许一个 JS 运行时创建多个高度隔离的 JS 运行环境(realm),每个 realm 具有独立的全局对象和内建对象。

这项特性提案时间为 2021 年 12 月,目前在Stage 3阶段 tc39.es/proposal-sh…

30分钟搞懂JS沙箱隔离什么是沙箱环境 在计算机安全中,沙箱(Sandbox)是一种用于隔离正在运行程序的安全机制,通常

  • evaluate(sourceText: string) 同步执行代码字符串,类似 eval()
  • importValue(specifier: string, bindingName: string) 异步执行代码字符串

7. Web Workers

Web Workers代码运行在独立的进程中,通信是异步的,无法获取当前程序一些属性或共享状态,且有一点无法不支持 DOM 操作,必须通过 postMessage 通知 UI 主线程来实现。

30分钟搞懂JS沙箱隔离什么是沙箱环境 在计算机安全中,沙箱(Sandbox)是一种用于隔离正在运行程序的安全机制,通常

以上就是实现JS沙箱隔离的一些思考点。在真实的业务应用中,没有最完美的方案,只有最合适的方案,还需要结合自身业务的特性做适合自己的选型。

转载自:https://juejin.cn/post/7410347763898597388
评论
请登录