Node.js应用设置安全的沙箱环境(2)

我们知道无论 eval 还是 function,执行时都会把作用域一层一层向上查找,如果找不到会一直到 global,那么利用 Proxy 的原理就是,让执行了代码在 sandobx 中找的到,以达到「防逃逸」的目的。

在浏览器中,还可以利用 iframe,创建一个再发安全的一些隔离环境,本文也着眼于 Node.js,在这里不做过多讨论。
在 Node.js 中呢,有没有其它选择?

或许没看到这儿之前你就已经想到了 VM,它是 Node.js 默认就提供的一个内建模块,VM 模块提供了一系列 API 用于在 V8 虚拟机环境中编译和运行代码。JavaScript 代码可以被编译并立即运行,或编译、保存然后再运行。

const vm = require('vm');
const script = new vm.Script('m + n');
const sandbox = { m: 1, n: 2 };
const context = new vm.createContext(sandbox);
script.runInContext(context);

执行上这的代码就能拿到结果 3,同时,通过 vm.Script 还能指定代码执行了「最大毫秒数」,超过指定的时长将终止执行并抛出一个异常

try {
 const script = new vm.Script('while(true){}',{ timeout: 50 });
 ....
} catch (err){
 //打印超时的 log
 console.log(err.message);
}

上面的脚本执行将会失败,被检测到超时并抛出异常,然后被 Try Cache 捕获到并打出 log,但同时需要注意的是 vm.Script 的 timeout 选项「只针对同步代有效」,而不包括是异步调用的时间,比如

const script = new vm.Script('setTimeout(()=>{},2000)',{ timeout: 50 });
 ....

上述代码,并不是会在 50ms 后抛出异常,因为 50ms 上边的代码同步执行肯定完了,而 setTimeout 所用的时间并不算在内,也就是说 vm 模块没有办法对异步代码直接限制执行时间。我们也不能额外通过一个 timer 去检查超时,因为检查了执行中的 vm 也没有方法去中止掉。

另外,在 Node.js 通过 vm.runInContext 看起来似乎隔离了代码执行环境,但实际上却很容易「逃逸」出去。

const vm = require('vm');
const sandbox = {};
const script = new vm.Script('this.constructor.constructor("return process")().exit()');
const context = vm.createContext(sandbox);
script.runInContext(context);

执行上边的代码,宿主程序立即就会「退出」,sandbox 是在 VM 之外的环境创建的,需 VM 中的代码的 this 指向的也是 sandbox,那么

//this.constructor 就是外所的 Object 构建函数
const ObjConstructor = this.constructor; 
//ObjConstructor 的 constructor 就是外包的 Function
const Function = ObjConstructor.constructor;
//创建一个函数,并执行它,返回全局 process 全局对象
const process = (new Function('return process'))(); 
//退出当前进程
process.exit(); 
      

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/449.html