成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

淺析 Node.js 的 vm 模塊以及運(yùn)行不信任代碼

894974231 / 2545人閱讀

摘要:下面我們介紹提供的模塊以及分析用它來(lái)運(yùn)行不信任代碼可能遇到的問(wèn)題。模塊模塊是內(nèi)置的核心模塊,它能讓我們編譯代碼和在指定的環(huán)境中運(yùn)行。上有一些開源的模塊用于運(yùn)行不信任代碼,例如,,等。

在一些系統(tǒng)中,我們希望給用戶提供插入自定義邏輯的能力,除了 RPCREST 之外,運(yùn)行客戶提供的代碼也是比較常用的方法,好處是可以極大地減少在網(wǎng)絡(luò)上的耗時(shí)。JavaScript 是一種非常流行而且容易上手的語(yǔ)言,因此,讓用戶用 JavaScript 來(lái)寫自定義邏輯是一個(gè)不錯(cuò)的選擇。下面我們介紹 Node.js 提供的 vm 模塊以及分析用它來(lái)運(yùn)行不信任代碼可能遇到的問(wèn)題。

vm 模塊

vm 模塊是 Node.js 內(nèi)置的核心模塊,它能讓我們編譯 JavaScript 代碼和在指定的環(huán)境中運(yùn)行。請(qǐng)看下面例子:

const util = require("util");
const vm = require("vm");

// 1. 創(chuàng)建一個(gè) vm.Script 實(shí)例, 編譯要執(zhí)行的代碼
const script = new vm.Script("globalVar += 1; anotherGlobalVar = 1; ");
// 2. 用于綁定到 context 的對(duì)象
const sandbox = {globalVar: 1};
// 3. 創(chuàng)建一個(gè) context, 并且把 sandbox 這個(gè)對(duì)象綁定到這個(gè)環(huán)境, 作為全局對(duì)象
const contextifiedSandbox = vm.createContext(sandbox);
// 4. 運(yùn)行上面編譯的代碼, context 是 contextifiedSandbox
const result = script.runInContext(contextifiedSandbox);

console.log(`sandbox === contextifiedSandbox ? ${sandbox === contextifiedSandbox}`);
// sandbox === contextifiedSandbox ? true
console.log(`sandbox: ${util.inspect(sandbox)}`);
// sandbox: { globalVar: 2, anotherGlobalVar: 1 }
console.log(`result: ${util.inspect(result)}`);
// result: 1

vm.Script 是一個(gè)類,用于創(chuàng)建代碼實(shí)例,后面可以多次運(yùn)行。

vm.createContext(sandbox) 用于 "contextify" 一個(gè)對(duì)象,根據(jù) ECMAScript 2015 語(yǔ)言規(guī)范,代碼的執(zhí)行需要一個(gè) execution context。這里的 "contextify",就是把傳進(jìn)去的對(duì)象與 V8 的一個(gè)新的 context 進(jìn)行關(guān)聯(lián)。這里所說(shuō)的關(guān)聯(lián),我的理解是,這個(gè) "contextified" 對(duì)象的屬性將會(huì)成為那個(gè) context 的全局屬性,同時(shí),在 context 下運(yùn)行代碼時(shí)產(chǎn)生的全局屬性也會(huì)成為這個(gè) "contextified" 對(duì)象的屬性。

script.runInContext(contextifiedSandbox) 就是使代碼在 contextifiedSandbox 這個(gè) context 中運(yùn)行,從上面的輸出可以看到,代碼運(yùn)行后,contextifiedSandbox 里面的屬性的值已經(jīng)被改變了,運(yùn)行結(jié)果是最后一個(gè)表達(dá)式的值。

除了上面幾個(gè)接口之外,vm 模塊還有一些更便捷的接口,例如 vm.runInContext(code, contextifiedSandbox[, options]),vm.runInNewContext(code[, sandbox][, options])等,詳細(xì)可看文檔。

外層如何得到代碼運(yùn)行結(jié)果

我們用 vm 運(yùn)行代碼的時(shí)候很可能需要得到一些結(jié)果,從上面的例子中可以看到,我們可以通過(guò)把結(jié)果作為最后一個(gè)表達(dá)式的值傳給外層,或者作為context 的屬性給外層使用,這在同步代碼里沒有問(wèn)題,但是假如結(jié)果需要依賴?yán)锩娴漠惒讲僮髂??這時(shí),我們可以通過(guò)在 context 里放一個(gè)回調(diào)函數(shù)。 下面是例子:

const util = require("util");
const vm = require("vm");

const sandbox = {globalVar: 1, setTimeout: setTimeout, cb: function(result) {
    console.log(result);
}};
vm.createContext(sandbox);
const script = new vm.Script(`
    setTimeout(function(){
        globalVar++;
        cb("async result");
    }, 1000);
`,{});
script.runInContext(sandbox);
console.log(`globalVar: ${sandbox.globalVar}`);
// globalVar: 1
// async result
代碼運(yùn)行時(shí)間限制

script.runInContext(contextifiedSandbox[, options]) 方法有一個(gè) timeout 選項(xiàng)可以設(shè)定代碼的運(yùn)行時(shí)間,如果超過(guò)時(shí)間就會(huì)拋出錯(cuò)誤,請(qǐng)看下面例子: 

const util = require("util");
const vm = require("vm");
const sandbox = {};
const contextifiedSandbox = vm.createContext(sandbox);
const script = new vm.Script("while(true){}");
const result = script.runInContext(contextifiedSandbox, {timeout: 1000});
// const result = script.runInContext(contextifiedSandbox, {timeout: 1000});
//                       ^
// Error: Script execution timed out.

再試試異步代碼,

const util = require("util");
const vm = require("vm");

const sandbox = {globalVar: 1, setTimeout: setTimeout, cb: function(result) {
    console.log(result);
}};
vm.createContext(sandbox);
const script = new vm.Script(`
    setTimeout(function(){
        globalVar++;
        cb("async result");
    }, 1000);
    globalVar;
`,{});
const result = script.runInContext(sandbox, {timeout: 500});
console.log(`result: ${result}`);
// result: 1
// async result

沒有錯(cuò)誤拋出,也就是說(shuō),這個(gè)選項(xiàng)并不能限制異步代碼的運(yùn)行時(shí)間,那應(yīng)該怎么去限制所有代碼的執(zhí)行時(shí)間呢,目前好像沒有接口終止 vm 代碼的運(yùn)行,如果有異步代碼長(zhǎng)時(shí)間不結(jié)束,很容易造成內(nèi)存泄露,目前可行的方案是使用子進(jìn)程去運(yùn)行代碼,如果超過(guò)限定時(shí)間還沒有結(jié)果,就殺掉該子進(jìn)程,另外,使用子進(jìn)程還可以更方便地對(duì)內(nèi)存等資源進(jìn)行限制。

定制 context 與安全問(wèn)題

在一個(gè)全新的 V8 context 里運(yùn)行代碼,里面包含了語(yǔ)言規(guī)范規(guī)定的內(nèi)置的一些函數(shù)和對(duì)象,如果我們想要一些語(yǔ)言規(guī)范之外的功能或者模塊,我們需要把相應(yīng)對(duì)象放到與這個(gè) context 關(guān)聯(lián)的對(duì)象里,例如在上面例子中的這句代碼:

const sandbox = {globalVar: 1, setTimeout: setTimeout, cb: function(result) {
    console.log(result);
}};

setTimeout 不是語(yǔ)言規(guī)范規(guī)定的內(nèi)置函數(shù), context 本身不提供,所以我們需要通過(guò)關(guān)聯(lián)的對(duì)象傳進(jìn)去。

然而,當(dāng)我們把一些模塊功能提供給 context 的時(shí)候,也同時(shí)帶入了更多的安全隱患,請(qǐng)看下面來(lái)自例子:

const util = require("util");
const vm = require("vm");

const sandbox = {};
vm.createContext(sandbox);
const script = new vm.Script(`
    // sandbox 的 constructor 是外層的 Object 類
    // Object 類的 constructor 是外層的 Function 類
    const OutFunction = this.constructor.constructor;
    // 于是, 利用外層的 Function 構(gòu)造一個(gè)函數(shù)就可以得到外層的全局 this
    const OutThis = (OutFunction("return this;"))();
    // 得到 require
    const require = OutThis.process.mainModule.require;
    // 試試
    require("fs");
`,{});
const result = script.runInContext(sandbox);
console.log(result === require("fs"));
// true

顯然,定制 context 的時(shí)候,任何一個(gè)傳進(jìn)去的對(duì)象或者函數(shù)都可能帶來(lái)上面的問(wèn)題,安全問(wèn)題真的有很多工作需要做。

Github 上有一些開源的模塊用于運(yùn)行不信任代碼,例如 sandbox,vm2,jailed等。查看這些項(xiàng)目的 issue 可以發(fā)現(xiàn),sandbox 和 jailed 都可以用類似上面的方法突破限制,而 vm2 對(duì)這方面做了防護(hù),其它方面也做了更多的安全工作,相對(duì)安全些。

生產(chǎn)中可以考慮在子進(jìn)程中運(yùn)行 vm2, 然后增加更低層的安全限制, 例如限制進(jìn)程的權(quán)限和使用 cgroups 進(jìn)行 IO,內(nèi)存等資源限制,這里不詳細(xì)討論。

總結(jié)

本文通過(guò)幾個(gè)例子介紹了 Node.js 的 vm 模塊以及使用 vm 模塊運(yùn)行不信任代碼可能遇到的問(wèn)題,并且對(duì)安全問(wèn)題給出了一些建議。

參考

vm
Allowing to terminate a vm context/script
V8 Embedder"s Guide
ECMAScript 2015 語(yǔ)言規(guī)范
sandbox/issues/50
vm2/issues/32
jailed/issues/33
cgroups

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/86763.html

相關(guān)文章

  • Node.js 應(yīng)用建立一個(gè)更安全沙箱環(huán)境

    摘要:當(dāng)運(yùn)行函數(shù)的時(shí)候,只能訪問(wèn)自己的本地變量和全局變量,不能訪問(wèn)構(gòu)造器被調(diào)用生成的上下文的作用域。如何建立一個(gè)更安全一些的沙箱通過(guò)上文的探究,我們并沒有找到一個(gè)完美的方案在建立安全的隔離的沙箱。 showImg(https://segmentfault.com/img/remote/1460000014575992); 有哪些動(dòng)態(tài)執(zhí)行腳本的場(chǎng)景? 在一些應(yīng)用中,我們希望給用戶提供插入自定義...

    cartoon 評(píng)論0 收藏0
  • NPM酷庫(kù):vm2,安全沙箱環(huán)境

    摘要:而標(biāo)準(zhǔn)庫(kù)中的是不安全的,用戶腳本可以輕易突破沙箱環(huán)境,獲取主程序的上述代碼在執(zhí)行時(shí),程序在第二行就直接退出,虛擬機(jī)環(huán)境中的代碼逃逸,獲得了主線程的變量,并調(diào)用,造成主程序非正常退出。 NPM酷庫(kù),每天兩分鐘,了解一個(gè)流行NPM庫(kù)。 今天我們要了解的庫(kù)是 vm2,則是一個(gè)Node.js 官方 vm 庫(kù)的替代品,主要解決了安全問(wèn)題。 不安全的vm 在Node.js官方標(biāo)準(zhǔn)庫(kù)中有一個(gè)vm庫(kù),...

    pkhope 評(píng)論0 收藏0
  • 保護(hù) Node.js 項(xiàng)目代碼

    摘要:而對(duì)于應(yīng)用越來(lái)越廣泛的而言,運(yùn)行的則是源代碼。通過(guò)查閱的相關(guān)代碼,可以發(fā)現(xiàn)字節(jié)碼的頭部保存著這些信息其中第項(xiàng)就是源代碼長(zhǎng)度。本文同時(shí)發(fā)表于作者個(gè)人博客保護(hù)項(xiàng)目的源代碼 SaaS(Software as a Service,軟件即服務(wù)),是一種通過(guò)互聯(lián)網(wǎng)提供軟件服務(wù)的模式。服務(wù)提供商會(huì)全權(quán)負(fù)責(zé)軟件服務(wù)的搭建、維護(hù)和管理,使得他們的客戶從這些繁瑣的工作中解放出來(lái)。對(duì)于許多中小型企業(yè)而言,S...

    Steven 評(píng)論0 收藏0
  • vue開發(fā)看這篇文章就夠了

    摘要:注意此處獲取的數(shù)據(jù)是更新后的數(shù)據(jù),但是獲取頁(yè)面中的元素是更新之前的鉤子函數(shù)說(shuō)明組件已經(jīng)更新,所以你現(xiàn)在可以執(zhí)行依賴于的操作。鉤子函數(shù)說(shuō)明實(shí)例銷毀 Vue -漸進(jìn)式JavaScript框架 介紹 vue 中文網(wǎng) vue github Vue.js 是一套構(gòu)建用戶界面(UI)的漸進(jìn)式JavaScript框架 庫(kù)和框架的區(qū)別 我們所說(shuō)的前端框架與庫(kù)的區(qū)別? Library 庫(kù),本質(zhì)上是一...

    fsmStudy 評(píng)論0 收藏0
  • 初識(shí)Node.js

    摘要:一旦替換已經(jīng)完成,該模塊將被完全棄用。用作錯(cuò)誤處理事件文件,由在標(biāo)準(zhǔn)功能上的簡(jiǎn)單包裝器提供所有模塊都提供這些對(duì)象。 Node.js簡(jiǎn)介 Node 定義 Node.js是一個(gè)建立在Chrome v8 引擎上的javascript運(yùn)行時(shí)環(huán)境 Node 特點(diǎn) 異步事件驅(qū)動(dòng) showImg(https://segmentfault.com/img/bVMLD1?w=600&h=237); no...

    lk20150415 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<