当前位置: 首页 > 后端技术 > Node.js

Node.js沙箱环境

时间:2023-04-04 00:59:47 Node.js

node官方文档中提到可以使用node的vm模块在沙箱环境中执行代码,沙箱环境隔离了代码的上下文。一个常见的用例是在沙盒环境中运行代码。沙盒代码使用不同的V8上下文,这意味着它具有与其余代码不同的全局对象。先看一个例子constvm=require('vm');leta=1;varresult=vm.runInNewContext('varb=2;a=3;a+b;',{a});console.日志(结果);//5console.log(a);//1console.log(typeofb);//在未定义的沙箱环境中执行的代码对外部代码没有影响,无论是新声明的变量b还是重新赋值的变量a。注意最后一行代码默认会加上return关键字,不需要手动添加。添加后,不会被静默忽略,而是会被执行报错。constvm=require('vm');leta=1;varresult=vm.runInNewContext('varb=2;a=3;returna+b;',{a});console.log(result);console.log(a);console.log(typeofb);evalmachine.<匿名>:1varb=2;一=3;返回a+b;^^^^^^SyntaxError:在Object.runInNewContext(vm.js:291:10)的createScript(vm.js:246:10)处的新脚本(vm.js:74:7)处的非法返回语句。<匿名>(/Users/xiji/workspace/learn/script.js:3:17)在Module._compile(internal/modules/cjs/loader.js:678:30)在Object.Module._extensions..js(内部/模块/cjs/loader.js:689:10)在Module.load(internal/modules/cjs/loader.js:589:32)在tryModuleLoad(internal/modules/cjs/loader.js:528:12)atFunction.Module._load(internal/modules/cjs/loader.js:520:3)atFunction.Module.runMain(internal/modules/cjs/loader.js:719:10)除了runInNewContext,vm还提供runInThisContext和runInContext两个方法可以用来执行代码runInThisContext不能指定contextconstvm=require('vm');letlocalVar='initialvalue';constvmResult=vm.runInThisContext('localVar+="vm";');console.log('vmResult:',vmResult);console.log('localVar:',localVar);console.log(global.localVar);由于不能访问local作用域,只能访问当前全局对象,所以上面代码会报错evalmachinebecauselocalValcannotbefound.:1localVar+="vm";^ReferenceError:localVarisnotdefined在evalmachine.:1:1在Script.runInThisContext(vm.js:91:20)在Object.runInThisContext(vm.js:298:38)在Object.(/Users/xiji/workspace/learn/script.js:3:21)在Module._compile(internal/modules/cjs/loader.js:678:30)在Object.Module._extensions..js(internal/modules/cjs/loader.js:689:10)在Module.load(internal/modules/cjs/loader.js:589:32)在tryModuleLoad(internal/modules/cjs/loader.js:528:12)在Function.Module._load(internal/modules/cjs/loader.js:520:3)在Function.Module.runMain(internal/modules/cjs/loader.js:719:10)如果我如果我们将要执行的代码改为直接赋值,可以正常运行,但是也会产生全局污染(globallocalVar变量)constvm=require('vm');letlocalVar='initialvalue';constvmResult=vm.runInThisContext('localVar="vm";');console.log('vmResult:',vmResult);//vmconsole.log('localVar:',localVar);//初始值console.log(global.localVar);//vmrunInContext与runInNewContext的区别在于传入的上下文参数。runInContext传入的context对象不为空,必须经过vm.createContext()处理,否则会报错。runInNewContext的上下文参数不是必需的。而且不需要经过vm.createContext处理。因为runInNewContext和runInContext有指定的上下文,所以不会像runInThisContext那样产生全局污染(不会产生全局的localVar变量)constvm=require('vm');letlocalVar='initialvalue';constvmResult=vm.runInNewContext('localVar="vm";');console.log('vmResult:',vmResult);//vmconsole.log('localVar:',localVar);//初始值console.log(global.localVar);//undefined当一个沙箱环??境需要执行多个脚本片段时,可以通过多次调用runInContext方法但传入相同的vm.createContext()返回值来实现。超时控制和错误捕获vm为要执行的代码提供了超时机制。RunInThisContext可以通过指定超时参数来作为示例。constvm=require('vm');letlocalVar='initialvalue';constvmResult=vm.runInThisContext('while(true){1};localVar="vm";',{timeout:1000});vm.js:91返回super.runInThisContext(...args);^错误:脚本执行超时。在Script.runInThisContext(vm.js:91:20)在Object.runInThisContext(vm.js:298:38)在Object.(/Users/xiji/workspace/learn/script.js:3:21)在Module._compile(internal/modules/cjs/loader.js:678:30)在Object.Module._extensions..js(internal/modules/cjs/loader.js:689:10)在Module.load(internal/模块/cjs/loader.js:589:32)在tryModuleLoad(internal/modules/cjs/loader.js:528:12)在Function.Module._load(internal/modules/cjs/loader.js:520:3)atFunction.Module.runMain(internal/modules/cjs/loader.js:719:10)atstartup(internal/bootstrap/node.js:228:19)可以通过trycatchconstvm=r捕获代码错误equire('vm');letlocalVar='initialvalue';try{constvmResult=vm.runInThisContext('while(true){1};localVar="vm";',{timeout:1000});}catch(e){console.error('executedcodetimeout');}除了立即执行代码外,vm的延迟执行也可以先编译,过一段时间再执行,这就需要提到vm。脚本其实无论是runInNewContext、runInThisContext还是runInThisContext,其实背后都创建了Script。从前面的报错信息可以看出,我们将使用vm.Script重写本文开头的例子constvm=require('vm');leta=1;varscript=newvm.Script('varb=2;a=3;a+b;');setTimeout(()=>{letresult=script.runInNewContext({a});控制台.log(result);//5console.log(a);//1console.log(typeofb);//undefined},300);除了vm.Script之外,node在9.6版本中添加了vm。模块也可以延迟执行。vm.Module主要用于支持ES6模块,其上下文在创建时已经绑定。关于vm.Module,是否仍然需要在命令行上使用flag来启用对node-experimental-vm-moduleindex.jsvmsafeasasandbox环境的支持?与eval相比,vm更安全,因为它隔离了当前上下文,但尽管如此,它仍然可以访问标准的JSAPI和全局的NodeJS环境,所以vm并不安全,这在官方文档中提到vmmoduleisnota安全机制。不要使用它来运行不受信任的代码请参阅以下示例constvm=require('vm');vm.runInNewContext("this.constructor.constructor('returnprocess')().exit()")("Theappgoeson...")//永远不会输出为了避免上述情况,上下文可以简化为只包含基本类型,如下所示letctx=Object.create(null);ctx.a=1;//ctx不能包含引用类型的属性vm.runInNewContext("this.constructor.constructor('returnprocess')().exit()",ctx);针对nativevm存在的这个问题,有人开发了vm2包,可以避免以上问题,但不能说vm2一定是安全的const{VM}=require('vm2');newVM().run('this.constructor.constructor("返回过程")().exit()');虽然执行上面的代码没有问题,但是因为vm2的timeout对于异步代码不起作用,下面的代码永远不会执行endconst{VM}=require('vm2');constvm=newVM({timeout:1000,sandbox:{}});vm.run('新承诺(()=>{})');即使你想通过重新定义Promise来禁用Promise,它仍然是绕过const{VM}=require('vm2');constvm=newVM({timeout:1000,sandbox:{Promise:function(){}}});vm.run('Promise=(asyncfunction(){})().constructor;newPromise(()=>{});');总结vm提供了一种隔离方式来执行不可信代码,但不是很彻底,对不可信代码最好的执行方式还是“物理隔离”,比如docker容器。参考https://nodejs.org/dist/lates...https://60devs.com/executing-...https://odino.org/eval-no-mor...https://segmentfault。com/a/11...

最新推荐
猜你喜欢