這篇文章給大家分享的是有關(guān)Node.js中沙箱環(huán)境的示例分析的內(nèi)容。小編覺得挺實(shí)用的,因此分享給大家做個參考,一起跟隨小編過來看看吧。
創(chuàng)新互聯(lián)云計(jì)算的互聯(lián)網(wǎng)服務(wù)提供商,擁有超過13年的服務(wù)器租用、四川電信機(jī)房托管、云服務(wù)器、雅安服務(wù)器托管、網(wǎng)站系統(tǒng)開發(fā)經(jīng)驗(yàn),已先后獲得國家工業(yè)和信息化部頒發(fā)的互聯(lián)網(wǎng)數(shù)據(jù)中心業(yè)務(wù)許可證。專業(yè)提供云主機(jī)、雅安服務(wù)器托管、域名注冊、VPS主機(jī)、云服務(wù)器、香港云服務(wù)器、免備案服務(wù)器等。
node官方文檔里提到node的vm模塊可以用來做沙箱環(huán)境執(zhí)行代碼,對代碼的上下文環(huán)境做隔離。
\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.
先看一個例子
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
沙箱環(huán)境中執(zhí)行的代碼對于外部代碼沒有產(chǎn)生任何影響,無論是新聲明的變量b,還是重新賦值的變量a。 注意最后一行的代碼默認(rèn)會被加上return關(guān)鍵字,因此無需手動添加,一旦添加的話不會靜默忽略,而是執(zhí)行報(bào)錯。
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);
如下所示
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)
除了runInNewContext外,vm還提供了runInThisContext和runInContext兩個方法都可以用來執(zhí)行代碼 runInThisContext無法指定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);
由于無法訪問本地的作用域,只能訪問到當(dāng)前的global對象,因此上面的代碼會因?yàn)檎也坏絣ocalVal而報(bào)錯
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)
如果我們把要執(zhí)行的代碼改成直接賦值的話就可以正常運(yùn)行了,但是也產(chǎn)生了全局污染(全局的localVar變量)
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在傳入context參數(shù)上與runInNewContext有所區(qū)別 runInContext傳入的context對象不為空而且必須是經(jīng)vm.createContext()處理過的,否則會報(bào)錯。 runInNewContext的context參數(shù)是非必須的,而且無需經(jīng)過vm.createContext處理。 runInNewContext和runInContext因?yàn)橛兄付╟ontext,所以不會向runInThisContext那樣產(chǎn)生全局污染(不會產(chǎn)生全局的localVar變量)
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
當(dāng)需要一個沙箱環(huán)境執(zhí)行多個腳本片段的時候,可以通過多次調(diào)用runInContext方法但是傳入同一個vm.createContext()返回值實(shí)現(xiàn)。
超時控制及錯誤捕獲
vm針對要執(zhí)行的代碼提供了超時機(jī)制,通過指定timeout參數(shù)即可以runInThisContext為例
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)
可以通過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'); }
延遲執(zhí)行
vm除了即時執(zhí)行代碼之外,也可以先編譯然后過一段時間再執(zhí)行,這就需要提到vm.Script了。其實(shí)無論是runInNewContext、runInThisContext還是runInThisContext,背后其實(shí)都創(chuàng)建了Script,從之前的報(bào)錯信息就可以看出來 接下來我們就用vm.Script來重寫本文開頭的例子
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);
除了vm.Script,node在9.6版本中新增了vm.Module也可以做到延遲執(zhí)行,vm.Module主要用來支持ES6 module,而且它的context在創(chuàng)建的時候就已經(jīng)綁定好了,關(guān)于vm.Module目前還需要在命令行使用flag來啟用支持
node --experimental-vm-module index.js
vm作為沙箱環(huán)境安全嗎?
vm相對于eval來說更安全一些,因?yàn)樗綦x了當(dāng)前的上下文環(huán)境了,但是盡管如此依然可以訪問標(biāo)準(zhǔn)的JS API和全局的NodeJS環(huán)境,因此vm并不安全,這個在官方文檔里就提到了
The vm module is not a security mechanism. Do not use it to run untrusted code
請看下面的例子
const vm = require('vm'); vm.runInNewContext("this.constructor.constructor('return process')().exit()") console.log("The app goes on...") // 永遠(yuǎn)不會輸出
為了避免上面這種情況,可以將上下文簡化成只包含基本類型,如下所示
let ctx = Object.create(null); ctx.a = 1; // ctx上不能包含引用類型的屬性 vm.runInNewContext("this.constructor.constructor('return process')().exit()", ctx);
針對原生vm存在的這個問題,有人開發(fā)了vm2包,可以避免上述問題,但是也不能說vm2就一定是安全的
const {VM} = require('vm2'); new VM().run('this.constructor.constructor("return process")().exit()');
雖然執(zhí)行上述代碼沒有問題,但是由于vm2的timeout對于異步代碼不起作用,所以下面的代碼永遠(yuǎn)不會執(zhí)行結(jié)束。
const { VM } = require('vm2'); const vm = new VM({ timeout: 1000, sandbox: {}}); vm.run('new Promise(()=>{})');
即使希望通過重新定義Promise的方式來禁用Promise的話,還是一個可以繞過的
const { VM } = require('vm2'); const vm = new VM({ timeout: 1000, sandbox: { Promise: function(){}} }); vm.run('Promise = (async function(){})().constructor;new Promise(()=>{});');
感謝各位的閱讀!關(guān)于“Node.js中沙箱環(huán)境的示例分析”這篇文章就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,讓大家可以學(xué)到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!
網(wǎng)站題目:Node.js中沙箱環(huán)境的示例分析
標(biāo)題鏈接:http://jinyejixie.com/article2/igoooc.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供標(biāo)簽優(yōu)化、云服務(wù)器、網(wǎng)站營銷、商城網(wǎng)站、網(wǎng)站制作、響應(yīng)式網(wǎng)站
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)