以下代码不会超时。
const {VM} = require('vm2');
new VM({timeout:1}).run(`
function main(){
while(1){}
}
new Proxy({}, {
getPrototypeOf(t){
global.main();
}
})`);
对此可能没有好的解决方案。 NodeJS 是单进程。 while(1){} 保护需要多进程,以便检查器不会被 while(1){} 阻塞。 也许如果我们可以重新点燃 SpiderNode 项目......
这是由Decontextify.value
使用instanceOf
测试来宾对象的原型引起的。 Node.js vm
模块当然不受此影响。 一个可怜的人的补丁可能是后门Proxy
以便我们可以从沙箱中检测任何代理对象,但理想的方法是永远不要相信它的任何对象和函数并将 contextify.js 放入沙箱中,使用原始值进行通信并处理引用来宾对象。
只是为了让人们看到,无浏览器使用子进程和消息传递与运行不受信任代码的子进程交谈。 这是一项设置任务,但一旦完成,您将免受while(1){}
攻击。 来源在这里。
这个项目的重点似乎是不产生一个单独的
操作系统进程。 否则有很多可用的解决方案
更安全。
否则,有许多更安全的解决方案可用。
哪个? 我不确定 NodeJS 中是否有更好的 VM2 替代品,至少我没见过
我只在子进程中运行不受信任的代码。 不能冒险在主 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 也有超时,它将使用相同的功能。