Vm2: 不受 while(1){}

创建于 2019-01-02  ·  19评论  ·  资料来源: patriksimek/vm2

以下代码不会超时。

const {VM} = require('vm2');
new VM({timeout:1}).run(`
        function main(){
        while(1){}
    }
    new Proxy({}, {
        getPrototypeOf(t){
            global.main();
        }
    })`);
bug confirmed help wanted

所有19条评论

对此可能没有好的解决方案。 NodeJS 是单进程。 while(1){} 保护需要多进程,以便检查器不会被 while(1){} 阻塞。 也许如果我们可以重新点燃 SpiderNode 项目......

这是由Decontextify.value使用instanceOf测试来宾对象的原型引起的。 Node.js vm模块当然不受此影响。 一个可怜的人的补丁可能是后门Proxy以便我们可以从沙箱中检测任何代理对象,但理想的方法是永远不要相信它的任何对象和函数并将 contextify.js 放入沙箱中,使用原始值进行通信并处理引用来宾对象。

只是为了让人们看到,无浏览器使用子进程和消息传递与运行不受信任代码的子进程交谈。 这是一项设置任务,但一旦完成,您将免受while(1){}攻击。 来源在这里

这个项目的重点似乎是不产生一个单独的
操作系统进程。 否则有很多可用的解决方案
更安全。

否则,有许多更安全的解决方案可用。

哪个? 我不确定 NodeJS 中是否有更好的 VM2 替代品,至少我没见过

这个库的维护者在 issue #80 中指出了这个替代方案:

https://github.com/laverdet/isolated-vm

我自己还没试过。

我只在子进程中运行不受信任的代码。 不能冒险在主 V8 实例中运行它,尤其是当您知道它是单线程时。

直接调用V8引擎的问题是缺少导入模块。 就我而言,我需要导入请求模块以允许 HTTP 操作。 它也必须是异步的。

我发现 vm2 + child_process 是一个很好的组合。

嘿,

我和@harelmo在 vm2 之上创建了一个不受while(1) {}

类似于 tian-ma 提到的,但我们使用 node.js 工作线程而不是子进程。

看看这里:https://www.npmjs.com/package/isolated-runtime,让我们知道你的想法:)

另一种解决方法是在沙箱中禁用Proxy ,对吗?

const vm = new VM({
    sandbox: {
        Proxy: undefined
    },
    timeout: 100
});

使用 Promise 还可以编写永不超时的代码:

"use strict";
const {VM} = require('vm2');
const untrusted = '(' + function(){
    Promise.resolve().then(a=>{
        while(1){}
    });
}+')()';
try{
    console.log(new VM({timeout:10}).run(untrusted));
}catch(x){
    console.log(x);
}

这里的问题是 .then 会将稍后在微任务队列中调用的函数排队。
为了解决这个问题,我们需要编写我们自己的 Promise.then 和相关函数或禁用 Promise。

这也有效:

const {VM} = require('vm2');

const script = '(' + function() {
    (async function x(){
        await {then: y=>y()};
        while(1){}
    })();
}+')()';

new VM({timeout:10}).run(script);

所以仅仅覆盖 Promise 是行不通的。
不知道如何解决这个问题。

虽然这仍然是开放的,但我们应该在 README 中添加说明以警告潜在用户。 这个漏洞允许人们逃离沙箱,从而破坏了库的目的。

自述文件已更新https://github.com/patriksimek/vm2/commit/77f5265ab53b87864a312156ee62b1082787e9b0#diff -04c6e90faac2675aa89e2176d2eec7d8。 而且我不知道如何使用它来逃避沙箱,您只能逃避脚本应该运行的预期时间范围。

也许这应该是它自己的问题,但为什么 NodeVM 不支持“超时”?

@crowder因为在 NodeVM 中存在 setTimeout、setInterval 和 setImmediate,所以很容易绕过超时。 也许还有其他原因,但我不使用 NodeVM,只使用 VM。

@XmiliaH不会实现超时至少防止意外的无限循环吗?
恶意的 while(true) + setTimeout 组合仍然是一个问题,但任何简单的意外无限循环都可以通过支持超时来处理

我担心的是 NodeVM 允许需要可能是主机模块的模块,并且在它们更改全局状态时超时可能很糟糕。 此外,NodeVM 返回一个模块,用户很可能会调用该模块公开的函数,但是超时仅适用于模块加载而不适用于函数调用。
如果你想调用一个带有超时的函数,你可以像这样自己实现它

function doWithTimeout(fn, timeout) {
    let ctx = CACHE.timeoutContext;
    let script = CACHE.timeoutScript;
    if (!ctx) {
        CACHE.timeoutContext = ctx = vm.createContext();
        CACHE.timeoutScript = script = new vm.Script('fn()', {
            filename: 'timeout_bridge.js',
            displayErrors: false
        });
    }
    ctx.fn = fn;
    try {
        return script.runInContext(ctx, {
            displayErrors: false,
            timeout
        });
    } finally {
        ctx.fn = null;
    }
}

好点@XmiliaH ,谢谢你的例子
我看到你的例子是使用 vm 包而不是 vm2
能够使用 vm2 并且仍然能够超时它会很好

@szydan VM2 使用我发布的代码来超时 VM。 如果 NodeVM 也有超时,它将使用相同的功能。

此页面是否有帮助?
0 / 5 - 0 等级