【问题标题】:nodejs: run module in sandboxnodejs:在沙箱中运行模块
【发布时间】:2015-08-20 21:45:10
【问题描述】:

我有这个基于回合制的 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();
        ...
    }
}

我希望 sandbox/creeateSandboxedInstance 环境照顾的是:

  • 播放器类不应授予对文件系统/互联网的访问权限
  • 播放器类不应访问应用全局变量
  • 应重置任何状态(如类变量)
  • 可能还有更多内容 :)

我认为我应该使用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 方法。有没有可能?

【问题讨论】:

标签: javascript node.js sandbox


【解决方案1】:

不要自己这样做。使用现有库。如果您要自己编写它,则必须处理很多问题。例如:您如何处理编写永无止境的 for 循环的用户?

How to run untrusted code serverside?


如果您打算自己编写它,那么是的,您将需要 vm 模块。

通过传入一个空的“沙盒”,您已经删除了所有全局变量。

script.runInNewContext({});

接下来,您需要弄清楚要如何处理永无止境的 for 循环。您必须创建一个新流程来处理这种情况。您是否创建 1 个进程来管理所有不受信任的代码?如果你这样做了,那么如果单个脚本挂起,你将不得不杀死所有不受信任的代码。您是否为每个不受信任的代码创建一个新进程?如果你这样做,那么你将不会对性能感到满意。创建一个新流程可能需要一两秒钟。您可以要求子进程“通知”它仍然存在的主进程。如果它未能在 5 秒内通知(或者无论您的阈值是什么,请终止该进程)。注意:script.runInNewContext 确实包含一个选项,可让您指定“超时”(如果代码花费的时间超过 X 秒 - 抛出异常),但问题是它允许异步代码(根据另一个 Stackoverflow 帖子),尽管您可以通过不将setTimeoutsetIntervalsetImmediate 引入范围来防御这种情况。但是,即使您将其设置为 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');

接下来,您需要弄清楚如何从主进程到子进程,然后再到 vm 进行通信。我不会进入进程之间的通信,因为你可以很容易地找到它。因此,通过调用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');

【讨论】:

  • 我认为如果需要太长时间,我必须取消该过程。我也为此创建了一个post :) 无论如何,我已经用webworker-threads 进行了一些测试,它们可以很容易地取消。因此,如果我在 webworker 中运行沙箱,它可能真的可以工作,我认为
  • 所以这给我带来了一个新问题:网络工作者是一个好的沙箱吗?
  • 我对该模块一无所知 - 话虽如此 - 它可能是创建单独进程的好方法,但可能不是设计为真正的“沙盒”。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-03-11
  • 1970-01-01
  • 2015-03-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多