nodejs:在沙箱中运行模块 [英] nodejs: run module in sandbox

查看:49
本文介绍了nodejs:在沙箱中运行模块的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有这个回合制 NodeJs 游戏应用程序,开发人员(任何人)都可以在其中提交玩家机器人.我的 NodeJS 应用程序将加载所有玩家并让他们互相对战.因为我对提交的代码一无所知,所以我需要在沙箱中运行它.

I have this turn-based NodeJs gaming app in which developers (anyone) can submit a player-robot. My NodeJS app will load all players and let them play against each other. Because I don't know anything about the code submitted I need to run it inside a sandbox.

例如,以下不受信任的代码可能如下所示:

For example, the following untrusted code might look like this:

let history = [];

export default class Player {
    constructor () {
        this.history = [];
    }

    move (info) {
        this.history.push(info);
   }

   done(result) {
       history.push({result: result, history: this.history});
   }
}

现在,在我的主应用程序中,我想做一些类似的事情

Now, in my main app I would like to do something like

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 环境处理的是:

What I would like the sandbox/creeateSandboxedInstance environment to take care of is:

  • 播放器类不应授予访问文件系统/互联网的权限
  • 播放器类不应访问应用程序全局变量
  • 任何状态都应该被重置(比如类变量)
  • 可能还有更多的东西:)

我认为我应该使用 vm 模块.大概是这样的:

I think that I should use the vm module. Something like this probably:

var vm = require('vm');

var script = new vm.Script('function move(info){ ... } ...', {conext});

var sandbox = script.runInNewContext();

script.move(..);  // or
sandbox.move(..);

但是,我无法让它工作,以至于我可以调用 move 方法.甚至有可能吗?

However, I cannot get it to work such that I can call the move method. Is something like even possible ?

推荐答案

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

Don't do this yourself. Use an existing library. There are quite a few issues you have to deal with if you were to write it yourself. For example: How do you handle a user writing a never ending for-loop?

如何在服务器端运行不受信任的代码?

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

If you are planning on writing it yourself then yes, you will need the vm module.

通过传入一个空的沙箱",您已经删除了所有全局变量.

By passing in an empty "sandbox" you have removed all global variables.

script.runInNewContext({});

接下来,您需要弄清楚如何处理永无止境的 for 循环.您必须创建一个新流程来处理这种情况.您是否创建了 1 个进程来管理所有不受信任的代码?如果您这样做了,那么如果单个脚本挂起,您将不得不终止所有不受信任的代码.您是否为每个不受信任的代码创建一个新流程?如果您这样做,那么您将不会对性能感到满意.创建一个新进程可能需要一两秒钟.您可以要求子进程通知"主进程它仍然活着.如果它未能在 5 秒内通知(或无论您的阈值是多少,请终止该进程).注意:script.runInNewContext 确实包含一个选项,可让您指定超时"(如果代码花费的时间超过 X 秒 - 抛出异常),但问题在于它允许异步代码(根据另一个 Stackoverflow 帖子),尽管您可以通过不在范围中引入 setTimeoutsetIntervalsetImmediate 来防御这种情况.但是,即使您将其设置为 1 秒,在该过程中的那一秒内也不能运行其他代码.因此,如果您有 1000 个脚本要运行,则最多可能需要 1000 秒(16 分钟)来运行它们.至少在他们自己的进程中运行每个会让它们并行运行.

Next you'll need to figure out how you want to handle the never ending for-loop. You'll have to create a new process to handle this scenario. Do you create 1 process to manage ALL untrusted code? If you do then you'll have to kill ALL untrusted code if a single script hangs. Do you create a new process for each untrusted code? If you do then you won't be happy with performance. Creating a new process can take a second or two. You could require the child process to "notify" the main process it's still alive. If it fails to notify within 5 seconds (or whatever your threshold is, kill the process). Note: script.runInNewContext does contain an option that lets you specify a "timeout" (if the code takes longer than X seconds - throw an exception), but the problem with that is it allows async code (according to another Stackoverflow post), although you could defend against that by not introducing setTimeout, setInterval, or setImmediate into the scope. However, even if you set it to 1 second, NO other code can run during that second in that process. So if you have 1000 scripts to run, it could take up to 1000 seconds (16 minutes) to run them all. At least running each in their own process will let them run in parallel.

以下是超时选项为何对您不起作用的示例:

Here's an example of why the timeout option won't work for you:

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,您可以立即执行代码.这让您可以设置全局变量:

Next you'll need to figure out how to communicate from your main process, into a child process and then into the vm. I'm not going to get into communicating between processes as you can find that pretty easily. So, by calling script.runInNewContext you are executing the code right then and there. Which lets you set global variables:

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:在沙箱中运行模块的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆