Home >Web Front-end >JS Tutorial >Detailed explanation of steps to use Node.js sandbox environment

Detailed explanation of steps to use Node.js sandbox environment

php中世界最好的语言
php中世界最好的语言Original
2018-05-21 14:25:512350browse

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!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn