js沙箱

沙箱,即 sandbox,顾名思义,就是让你的程序跑在一个隔离的环境下,不对外界的其他程序造成影响,通过创建类似沙盒的独立作业环境,在其内部运行的程序并不能对硬盘产生永久性的影响。

使用场景

  • 解析或执行不可信的 JS
  • 隔离被执行代码的执行环境
  • 对执行代码中可访问对象进行限制

实现方式

with + new Function

with 的块级作用域下,变量访问会优先查找你传入的参数对象,之后再往上找,所以相当于你变相监控到了代码中的“变量访问”,结合 proxy 代理可以监听所有数据的修改和访问

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
function proxyFanctory(obj) {
const target = new Proxy(obj, {
get(target, p) {
if (p === Symbol.unscopables) {
return undefined
}
if (target[p] === undefined) {
return undefined
}
return target[p]
},
set(target, p, value) {
target[p] = value
},
})

return target
}

function runWithSanbox(code) {
const context = proxyFanctory({ a: 'test', b: {} })
const codeSrc = `with(sandbox){${code}}`
return new Function('sandbox', codeSrc).call(context, context)
}

runWithSanbox(
'this.b.__proto__.toString=function(){console.log("__proto__.toString")};console.log(a, this)'
) // test Proxy {a: "test"}
const t = {}
t.toString() // __proto__.toString
需要解决的问题
  • proxy 无法监听深层次数据的访问,可以通过访问原型链的方式,实现沙箱逃逸(代码分析)
  • window 等固有对象调用(iframe 创建隔离的上下文)

借助 iframe 实现

sandboxh5 的提出的一个新属性, 启用方式就是在 iframe 标签中使用 sandbox 属性

1
<iframe sandbox src="url"></iframe>
限制
  • script 脚本不能执行, allow-scripts 属性
  • 不能发送 ajax 请求,allow-same-origin 允许同域请求

具体参考 iframe

Web Worker

Web Worker 子线程的形式也是一种天然的沙箱隔离

限制
  • 出于线程安全设计考虑,Web Worker 不支持 DOM 操作,必须通过 postMessage 通知 UI 主线程来实现。
  • Web Worker 无法访问 windowdocument 之类的浏览器全局对象。

通过代理将具体的渲染实现再转发给原 WorkerDomNodeImpl.js 逻辑来实现 DOM 的实际更新。

参考