简单谈谈JS中的沙箱
沙箱(sandbox)是一种安全机制, 为运行中的程序提供的隔离环境。通常是作为一些来源不可信、具破坏力或无法判定程序意图的程序提供实验之用。沙盒通常严格控制其中的程序所能访问的资源,比如,沙盒可以提供用后即回收的磁盘及内存空间。
JS 中沙箱的使用场景
前端 JS 中也会有应用到沙箱的时候,毕竟有时候你要获取到的是第三方的 JS 文件或数据?而这数据又是不一定可信的时候,创建沙箱,做好保险工作尤为重要
- jsonp:解析服务器所返回的 jsonp 请求时,如果不信任 jsonp 中的数据,可以通过创建沙箱的方式来解析获取数据;(TSW 中处理 jsonp 请求时,创建沙箱来处理和解析数据);
- 执行第三方 js:当你有必要执行第三方 js 的时候,而这份 js 文件又不一定可信的时候;
- 在线代码编辑器:相信大家都有使用过一些在线代码编辑器,而这些代码的执行,基本都会放置在沙箱中,防止对页面本身造成影响;(例如:https://codesandbox.io/s/new)
- vue 模板中表达式计算:vue 模板中表达式的计算被放在沙盒中,只能访问全局变量的一个白名单,如 Math 和 Date 。你不能够在模板表达式中试图访问用户定义的全局变量
总而言之:当你要解析或执行不可信的 JS 的时候,当你要隔离被执行代码的执行环境的时候,当你要对执行代码中可访问对象进行限制的时候,沙箱就派上用场了
JS 沙箱实现
Function
Function
构造函数创建一个新的 Function
对象。直接调用此构造函数可用动态创建函数,但会遇到和 eval
类似的的安全问题和(相对较小的)性能问题。然而,与 eval
不同的是,Function
创建的函数只能在全局作用域中运行。
eval
eval()
函数会将传入的字符串当做 JavaScript 代码进行执行,eval()
是一个危险的函数, 它使用与调用者相同的权限执行代码。如果你用 eval()
运行的字符串代码被恶意方(不怀好意的人)修改,您最终可能会在您的网页/扩展程序的权限下,在用户计算机上运行恶意代码。更重要的是,第三方代码可以看到某一个 eval()
被调用时的作用域,这也有可能导致一些不同方式的攻击。相似的 Function
就不容易被攻击。
vm
vm 模块支持在 V8 虚拟机上下文中编译和运行代码。 vm 模块不是一种安全机制。不要使用它来运行不受信任的代码。
const { Script, createContext } = require("vm");
/**
*
* @param {string} code 需要执行的代码, 执行的代码被包括在一个自执行函数中
* @param {object} context 设置执行代码的上下文对象,默认包含 resolve: 用于代码返回值, reject: 用于代码抛出异常
* @param {object} options 可选参数
* @returns 执行代码返回值
*/
async function runScript(code, context = {}, options = {}) {
return new Promise((resolve, reject) => {
const { timeout = 120 * 1000, breakOnSigint = true } = options;
const script = new Script(`(async()=>{${code}})()`);
script.runInContext(
createContext({
...context,
resolve,
reject,
}),
{
timeout,
breakOnSigint,
}
);
});
}
vm2
vm2 是一个沙箱,可以使用列入白名单的 Node 的内置模块运行不受信任的代码, 它使用内部 VM 模块来创建安全上下文,它使用代理来防止逃离沙箱, 它覆盖了内置的 require 来控制对模块的访问.