VM Guard
- 作者:
- 苍石
- 进度:
- 测试
- 项目开始时间:
- 2021-06-09
- 项目结束时间:
- 项目发表时间:
- 2021-06-11 16:41:49
- 最后编辑于:
- 4 年前
- 标签:
- NODEJSJAVASCRIPTVM
VM Guard for NodeJS
A more secure multi thread sandbox for NodeJS.
VM Guard是基于VM2的NodeJS沙箱环境,用于解决一些在VM2中不能解决的问题
与 VM2 不同的特征
VM2是一款作用于Javascript的沙箱环境(其开源地址)
- 多进程增加运行沙箱代码的运行速率
- 新增
timeout(超时)可遏制在NodeJS环境下VM2中NodeVM所不能解决的恶意代码,如:while (true) {} - 可以限定每个进程的 CPU 或者 内存 占用(仅支持
Linux) vm-guard重写了vm2中的require实现,可以通过allowedModules属性自定义能够使用的模块vm-guard也对全局变量作出了限制,可以通过allowedVariables限制能使用的全局变量,比如__dirname- 支持链式
require(通过compilePath指定),在沙箱中被运行的代码如果依赖了其它代码,其引用的代码也会被运行在沙箱中,受到限制// runner.js const { runInProcess } = require('vm-guard'); (async () => { const fs = await runInProcess( ` const a = require('./a'); // 引入依赖 ./a.js module.exports = a; `, { compilePath: ['.*'], // 指定所有依赖都在沙箱执行 allowedModules: ['^\\..*'] // 只允许引用相对路径的依赖 } ); // 在运行到这步之前会报错:Error: Access denied, require failed: fs console.log(fs); })(); // a.js const fs = require('fs'); // 依赖 fs 模块(这是非法的引用) module.exports = fs; - 增加一个
@@vm-guard模块,其中包含一个run(script, options ?:)的方法,该模块只能在沙箱中调用,可以通过@@vm-guard引入
与 VM2 的兼容
VM Guard可以使用所有VM2中NodeVM的配置选项- 除此之外提供以下额外选项
选项名 默认值 描述 concurrency2并发数量 timeout1000包含异步/同步操作的超时时间(单位 ms) memoryQuota100最大能使用的内存(单位 m) cpuQuota0.5cpu 资源配额(百分比) globalAsyncfalse支持在代码块最外层使用 awaitnoHardwareLimitfalse取消所有的硬件限制,如 CPU,内存 legacyRequirefalse使用 vm2提供的require方法,默认使用VmGuard提供给的require方法allowedModules[]允许使用的模块,支持正则,不限定内部或外部,只有在 legacyRequire为false时生效allowedVariables[]允许使用的真实的 Node 全局变量,支持正则,如果指定 'process',那么process.exit()便可以退出程序allowInnerRunnertrue允许在沙箱中再开一个沙箱运行代码,一般通过 const {run} = require('@@vm-guard')来使用innerRunnerName@@vm-guard多重沙箱的模块名 compilePath[]需要运行在沙箱的依赖列表 compatibleRequirefalse兼容 vm2的require与legacyRequire不同的是allowedModules也会同时生效
简单安装
npm i vm-guard --save
简单使用
const { VmGuard } = require('vm-guard');
(async () => {
const vm = new VmGuard({
sandbox: {
a: ' test'
}, // vm2 的 options
concurrency: 2 // 并发限制
});
try {
await Promise.all([
// 以下输出由于在多进程下,不会以特定顺序输出
vm.run('console.log(\'1\' + a)'),
vm.run('console.log(\'2\' + a)'),
vm.run('console.log(\'3\' + a)'),
vm.run('console.log(\'4\' + a)'),
vm.run('console.log(\'5\' + a)'),
// 运行恶意代码会导致超时
vm.run(`
while(true){}
`)
]);
} catch (e) {
// 捕获超时或其它异常
console.log(e);
}
// 也可以在一段时间后运行,其子进程会自动管理,调度
setTimeout(() => {
vm.run('console.log(\'6\' + a)');
vm.run('console.log(\'17\' + a)');
vm.run('console.log(\'18\' + a)');
}, 2000);
})();
使用多进程的限制
在使用多进程的前提下,配置属性会以序列化的形式在进程之间传递,所以配置文件不能使用非序列化的内容,如 function, class, regexp 等等。
为了解决这一问题,我们实现了链式的
require定义,可以通过require将一些想要传递的方法等放在一个独立的文件里,然后通过require去引用。

留言 ( 0 )