nodejs: 运行 沙箱中的模块
nodejs: run module in sandbox
我有这个基于回合制的 NodeJs 游戏应用程序,开发人员(任何人)都可以在其中提交玩家机器人。我的 NodeJS 应用程序将加载所有玩家并让他们互相对战。因为我对提交的代码一无所知,所以我需要 运行 它在沙箱中。
例如,以下不受信任的代码可能如下所示:
let history = [];
export default class Player {
constructor () {
this.history = [];
}
move (info) {
this.history.push(info);
}
done(result) {
history.push({result: result, history: this.history});
}
}
现在,在我的主应用程序中,我想做一些类似
的事情
import Player1 from 'sandbox/player1';
import Player2 from 'sandbox/player2';
....
for (let outer = 0; outer < 10; outer ++) {
let player1 = creeateSandboxedInstance(Player1);
let player2 = creeateSandboxedInstance(Player2);
for(let inner = 0; inner < 1000000; inner ++) {
...
let move1 = player1.move();
let move2 = player2.doMove();
...
}
}
我希望沙箱/creeateSandboxedInstance
环境处理的是:
- 玩家 class 不应授予对文件系统/互联网的访问权限
- 玩家 class 不应访问应用程序全局变量
- 应重置任何状态(如 class 变量)
- 可能还有更多 :)
我认为我应该使用vm module。大概是这样的:
var vm = require('vm');
var script = new vm.Script('function move(info){ ... } ...', {conext});
var sandbox = script.runInNewContext();
script.move(..); // or
sandbox.move(..);
但是,我无法让它工作,因此我无法调用 move
方法。甚至可能吗?
不要自己做。使用现有的库。如果您要自己编写,则必须处理很多问题。例如:您如何处理用户编写永无止境的 for 循环?
How to run untrusted code serverside?
如果您打算自己编写,那么是的,您将需要 vm
模块。
通过传入一个空 "sandbox",您已删除所有全局变量。
script.runInNewContext({});
接下来您需要弄清楚您希望如何处理永无止境的 for 循环。您必须创建一个新流程来处理这种情况。您是否创建了 1 个进程来管理所有不受信任的代码?如果你这样做,那么如果一个脚本挂起,你将不得不杀死所有不受信任的代码。您是否为每个不受信任的代码创建一个新进程?如果这样做,那么您将不会对性能感到满意。创建一个新进程可能需要一两秒钟。您可以要求子进程 "notify" 仍然存在的主进程。如果它未能在 5 秒内通知(或任何您的阈值,则终止该进程)。注意:script.runInNewContext
确实包含一个允许您指定 "timeout" 的选项(如果代码花费的时间超过 X 秒 - 抛出异常),但问题是它允许异步代码(根据另一个Whosebug post),尽管您可以通过不将 setTimeout
、setInterval
或 setImmediate
引入范围来防御这种情况。但是,即使您将它设置为 1 秒,在该过程的那一秒内,其他代码也不能 运行。因此,如果您有 1000 个 运行 脚本,可能需要 1000 秒(16 分钟)才能将它们全部 运行。至少 运行 在各自的进程中使它们 运行 并行。
以下是超时选项对您不起作用的示例:
var script = new vm.Script('move = function move(info) { for(var i = 0; i < 100000; i++) { console.log(i); } }');
var sandbox = { move: null, console: console };
var result = script.runInNewContext(sandbox, { timeout: 1 });
sandbox.move('woah');
接下来您需要弄清楚如何从主进程到子进程再到虚拟机进行通信。我不打算讨论进程之间的通信,因为您可以很容易地找到它。因此,通过调用 script.runInNewContext
您可以立即执行代码。它允许您设置全局变量:
var script = new vm.Script('move = function move(info) { console.log("test: " + info); }');
var sandbox = { move: null, console: console };
var result = script.runInNewContext(sandbox);
sandbox.move('success');
我有这个基于回合制的 NodeJs 游戏应用程序,开发人员(任何人)都可以在其中提交玩家机器人。我的 NodeJS 应用程序将加载所有玩家并让他们互相对战。因为我对提交的代码一无所知,所以我需要 运行 它在沙箱中。
例如,以下不受信任的代码可能如下所示:
let history = [];
export default class Player {
constructor () {
this.history = [];
}
move (info) {
this.history.push(info);
}
done(result) {
history.push({result: result, history: this.history});
}
}
现在,在我的主应用程序中,我想做一些类似
的事情import Player1 from 'sandbox/player1';
import Player2 from 'sandbox/player2';
....
for (let outer = 0; outer < 10; outer ++) {
let player1 = creeateSandboxedInstance(Player1);
let player2 = creeateSandboxedInstance(Player2);
for(let inner = 0; inner < 1000000; inner ++) {
...
let move1 = player1.move();
let move2 = player2.doMove();
...
}
}
我希望沙箱/creeateSandboxedInstance
环境处理的是:
- 玩家 class 不应授予对文件系统/互联网的访问权限
- 玩家 class 不应访问应用程序全局变量
- 应重置任何状态(如 class 变量)
- 可能还有更多 :)
我认为我应该使用vm module。大概是这样的:
var vm = require('vm');
var script = new vm.Script('function move(info){ ... } ...', {conext});
var sandbox = script.runInNewContext();
script.move(..); // or
sandbox.move(..);
但是,我无法让它工作,因此我无法调用 move
方法。甚至可能吗?
不要自己做。使用现有的库。如果您要自己编写,则必须处理很多问题。例如:您如何处理用户编写永无止境的 for 循环?
How to run untrusted code serverside?
如果您打算自己编写,那么是的,您将需要 vm
模块。
通过传入一个空 "sandbox",您已删除所有全局变量。
script.runInNewContext({});
接下来您需要弄清楚您希望如何处理永无止境的 for 循环。您必须创建一个新流程来处理这种情况。您是否创建了 1 个进程来管理所有不受信任的代码?如果你这样做,那么如果一个脚本挂起,你将不得不杀死所有不受信任的代码。您是否为每个不受信任的代码创建一个新进程?如果这样做,那么您将不会对性能感到满意。创建一个新进程可能需要一两秒钟。您可以要求子进程 "notify" 仍然存在的主进程。如果它未能在 5 秒内通知(或任何您的阈值,则终止该进程)。注意:script.runInNewContext
确实包含一个允许您指定 "timeout" 的选项(如果代码花费的时间超过 X 秒 - 抛出异常),但问题是它允许异步代码(根据另一个Whosebug post),尽管您可以通过不将 setTimeout
、setInterval
或 setImmediate
引入范围来防御这种情况。但是,即使您将它设置为 1 秒,在该过程的那一秒内,其他代码也不能 运行。因此,如果您有 1000 个 运行 脚本,可能需要 1000 秒(16 分钟)才能将它们全部 运行。至少 运行 在各自的进程中使它们 运行 并行。
以下是超时选项对您不起作用的示例:
var script = new vm.Script('move = function move(info) { for(var i = 0; i < 100000; i++) { console.log(i); } }');
var sandbox = { move: null, console: console };
var result = script.runInNewContext(sandbox, { timeout: 1 });
sandbox.move('woah');
接下来您需要弄清楚如何从主进程到子进程再到虚拟机进行通信。我不打算讨论进程之间的通信,因为您可以很容易地找到它。因此,通过调用 script.runInNewContext
您可以立即执行代码。它允许您设置全局变量:
var script = new vm.Script('move = function move(info) { console.log("test: " + info); }');
var sandbox = { move: null, console: console };
var result = script.runInNewContext(sandbox);
sandbox.move('success');