Home > Article > Web Front-end > Detailed explanation of steps to use Node.js sandbox environment
This time I will bring you Node.js Detailed explanation of the steps for using the sandbox environment, what are the precautions for using the Node.js sandbox environment, the following is a practical case, one Get up and take a look.
Node’s official documentation mentions that node’s vm module can be used to execute code in a sandbox environment and isolate the context of the code.
\A common use case is to run the code in a sandboxed environment. The sandboxed code uses a different V8 Context, meaning that it has a different global object than the rest of the code.
Let’s look at an example first
const vm = require('vm'); let a = 1; var result = vm.runInNewContext('var b = 2; a = 3; a + b;', {a}); console.log(result); // 5 console.log(a); // 1 console.log(typeof b); // undefined
The code executed in the sandbox environment has no impact on the external code, whether it is the newly declared variable b or the reassigned variable a. Note that the last line of code will be added with the return keyword by default, so there is no need to add it manually. Once added, it will not be silently ignored, but an error will be reported.
const vm = require('vm'); let a = 1; var result = vm.runInNewContext('var b = 2; a = 3; return a + b;', {a}); console.log(result); console.log(a); console.log(typeof b);
As shown below
evalmachine.<anonymous>:1 var b = 2; a = 3; return a + b; ^^^^^^ SyntaxError: Illegal return statement at new Script (vm.js:74:7) at createScript (vm.js:246:10) at Object.runInNewContext (vm.js:291:10) at Object.<anonymous> (/Users/xiji/workspace/learn/script.js:3:17) at Module._compile (internal/modules/cjs/loader.js:678:30) at Object.Module._extensions..js (internal/modules/cjs/loader.js:689:10) at Module.load (internal/modules/cjs/loader.js:589:32) at tryModuleLoad (internal/modules/cjs/loader.js:528:12) at Function.Module._load (internal/modules/cjs/loader.js:520:3) at Function.Module.runMain (internal/modules/cjs/loader.js:719:10)
In addition to runInNewContext, vm also provides two methods, runInThisContext and runInContext, both of which can be used to execute code. runInThisContext cannot specify context
const vm = require('vm'); let localVar = 'initial value'; const vmResult = vm.runInThisContext('localVar += "vm";'); console.log('vmResult:', vmResult); console.log('localVar:', localVar); console.log(global.localVar);
Because it cannot Accessing the local scope can only access the current global object, so the above code will report an error because it cannot find localVal
evalmachine.<anonymous>:1 localVar += "vm"; ^ ReferenceError: localVar is not defined at evalmachine.<anonymous>:1:1 at Script.runInThisContext (vm.js:91:20) at Object.runInThisContext (vm.js:298:38) at Object.<anonymous> (/Users/xiji/workspace/learn/script.js:3:21) at Module._compile (internal/modules/cjs/loader.js:678:30) at Object.Module._extensions..js (internal/modules/cjs/loader.js:689:10) at Module.load (internal/modules/cjs/loader.js:589:32) at tryModuleLoad (internal/modules/cjs/loader.js:528:12) at Function.Module._load (internal/modules/cjs/loader.js:520:3) at Function.Module.runMain (internal/modules/cjs/loader.js:719:10)
If we change the code to be executed to direct assignment, it can run normally , but it also produces global pollution (global localVar variable)
const vm = require('vm'); let localVar = 'initial value'; const vmResult = vm.runInThisContext('localVar = "vm";'); console.log('vmResult:', vmResult); // vm console.log('localVar:', localVar); // initial value console.log(global.localVar); // vm
runInContext is different from runInNewContext in the context parameter passed in. The context object passed in by runInContext is not empty and must be passed through vm.createContext( ), otherwise an error will be reported. The context parameter of runInNewContext is optional and does not need to be processed by vm.createContext. Because runInNewContext and runInContext have a specified context, they will not cause global pollution like runInThisContext (no global localVar variables will be generated)
const vm = require('vm'); let localVar = 'initial value'; const vmResult = vm.runInNewContext('localVar = "vm";'); console.log('vmResult:', vmResult); // vm console.log('localVar:', localVar); // initial value console.log(global.localVar); // undefined
When a sandbox environment is required to execute multiple script fragments, you can use multiple Call the runInContext method twice but pass in the same vm.createContext() return value implementation.
Timeout control and error capture
vm provides a timeout mechanism for the code to be executed. By specifying the timeout parameter, you can runInThisContext as an example
const vm = require('vm'); let localVar = 'initial value'; const vmResult = vm.runInThisContext('while(true) { 1 }; localVar = "vm";', { timeout: 1000});
vm.js:91 return super.runInThisContext(...args); ^ Error: Script execution timed out. at Script.runInThisContext (vm.js:91:20) at Object.runInThisContext (vm.js:298:38) at Object.<anonymous> (/Users/xiji/workspace/learn/script.js:3:21) at Module._compile (internal/modules/cjs/loader.js:678:30) at Object.Module._extensions..js (internal/modules/cjs/loader.js:689:10) at Module.load (internal/modules/cjs/loader.js:589:32) at tryModuleLoad (internal/modules/cjs/loader.js:528:12) at Function.Module._load (internal/modules/cjs/loader.js:520:3) at Function.Module.runMain (internal/modules/cjs/loader.js:719:10) at startup (internal/bootstrap/node.js:228:19)
You can catch code errors through try catch
const vm = require('vm'); let localVar = 'initial value'; try { const vmResult = vm.runInThisContext('while(true) { 1 }; localVar = "vm";', { timeout: 1000 }); } catch(e) { console.error('executed code timeout'); }
Delayed execution
In addition to executing code immediately, vm also You can compile it first and then execute it after a while. This requires mentioning vm.Script. In fact, whether it is runInNewContext, runInThisContext or runInThisContext, Script is actually created behind it. It can be seen from the previous error message. Next, we will use vm.Script to rewrite the example at the beginning of this article
const vm = require('vm'); let a = 1; var script = new vm.Script('var b = 2; a = 3; a + b;'); setTimeout(() => { let result = script.runInNewContext({a}); console.log(result); // 5 console.log(a); // 1 console.log(typeof b); // undefined }, 300);
Except vm. Script and node are added in version 9.6. vm.Module can also be delayed in execution. vm.Module is mainly used to support ES6 modules, and its context is already bound when it is created. Regarding vm.Module, you still need to use flag on the command line to enable support for
node --experimental-vm-module index.js
vm as a sandbox environmentSafety?
vm is safer than eval because it isolates the current context, but it can still access the standard JS API and the global NodeJS environment, so vm It is not safe. This is mentioned in the official documentation
The vm module is not a security mechanism. Do not use it to run untrusted code
Please see below Example
const vm = require('vm'); vm.runInNewContext("this.constructor.constructor('return process')().exit()") console.log("The app goes on...") // 永远不会输出
In order to avoid the above situation, the context can be simplified to only contain basic types, as shown below
let ctx = Object.create(null); ctx.a = 1; // ctx上不能包含引用类型的属性 vm.runInNewContext("this.constructor.constructor('return process')().exit()", ctx);
To address this problem of native vm, someone developed the vm2 package, The above problems can be avoided, but it cannot be said that vm2 is necessarily safe
const {VM} = require('vm2'); new VM().run('this.constructor.constructor("return process")().exit()');
Although there is no problem in executing the above code, because the timeout of vm2 does not work for asynchronous code, the following code will never end execution. .
const { VM } = require('vm2'); const vm = new VM({ timeout: 1000, sandbox: {}}); vm.run('new Promise(()=>{})');
Even if you want to disable Promise by redefining Promise, there is still a way to bypass it
const { VM } = require('vm2'); const vm = new VM({ timeout: 1000, sandbox: { Promise: function(){}} }); vm.run('Promise = (async function(){})().constructor;new Promise(()=>{});');
I believe you have mastered the method after reading the case in this article. Please pay attention to php for more exciting things. Other related articles on the Chinese website!
Recommended reading:
How to deal with the 404 error reported by Tomcat when the Vue project webpack is packaged and deployedManagement
vue addRoutes Detailed explanation of the steps to implement dynamic permission routing menu
The above is the detailed content of Detailed explanation of steps to use Node.js sandbox environment. For more information, please follow other related articles on the PHP Chinese website!