<span id="mktg5"></span>

<i id="mktg5"><meter id="mktg5"></meter></i>

        <label id="mktg5"><meter id="mktg5"></meter></label>
        最新文章專題視頻專題問答1問答10問答100問答1000問答2000關(guān)鍵字專題1關(guān)鍵字專題50關(guān)鍵字專題500關(guān)鍵字專題1500TAG最新視頻文章推薦1 推薦3 推薦5 推薦7 推薦9 推薦11 推薦13 推薦15 推薦17 推薦19 推薦21 推薦23 推薦25 推薦27 推薦29 推薦31 推薦33 推薦35 推薦37視頻文章20視頻文章30視頻文章40視頻文章50視頻文章60 視頻文章70視頻文章80視頻文章90視頻文章100視頻文章120視頻文章140 視頻2關(guān)鍵字專題關(guān)鍵字專題tag2tag3文章專題文章專題2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章專題3
        問答文章1 問答文章501 問答文章1001 問答文章1501 問答文章2001 問答文章2501 問答文章3001 問答文章3501 問答文章4001 問答文章4501 問答文章5001 問答文章5501 問答文章6001 問答文章6501 問答文章7001 問答文章7501 問答文章8001 問答文章8501 問答文章9001 問答文章9501
        當(dāng)前位置: 首頁 - 科技 - 知識百科 - 正文

        應(yīng)用沙箱環(huán)境實戰(zhàn)使用

        來源:懂視網(wǎng) 責(zé)編:小采 時間:2020-11-27 19:40:24
        文檔

        應(yīng)用沙箱環(huán)境實戰(zhàn)使用

        應(yīng)用沙箱環(huán)境實戰(zhàn)使用:這次給大家?guī)響?yīng)用沙箱環(huán)境實戰(zhàn)使用,應(yīng)用沙箱環(huán)境實戰(zhàn)使用的注意事項有哪些,下面就是實戰(zhàn)案例,一起來看一下。有哪些動態(tài)執(zhí)行腳本的場景?在一些應(yīng)用中,我們希望給用戶提供插入自定義邏輯的能力,比如 Microsoft 的 Office 中的 VBA,比如一些游戲中的
        推薦度:
        導(dǎo)讀應(yīng)用沙箱環(huán)境實戰(zhàn)使用:這次給大家?guī)響?yīng)用沙箱環(huán)境實戰(zhàn)使用,應(yīng)用沙箱環(huán)境實戰(zhàn)使用的注意事項有哪些,下面就是實戰(zhàn)案例,一起來看一下。有哪些動態(tài)執(zhí)行腳本的場景?在一些應(yīng)用中,我們希望給用戶提供插入自定義邏輯的能力,比如 Microsoft 的 Office 中的 VBA,比如一些游戲中的

        上述代碼沒有問題順利執(zhí)行了,eval 是全局對象的一個函數(shù)屬性,執(zhí)行的代碼擁有著和應(yīng)程中其它正常代碼一樣的的權(quán)限,它能訪問「執(zhí)行上下文」中的局部變量,也能訪問所有「全局變量」,在這個場景下,它是一個非常危險的函數(shù)。

        再來看看 Functon,通過 Function 構(gòu)造器,我們可以動態(tài)的創(chuàng)建一個函數(shù),然后執(zhí)行它

        const sum = new Function('m', 'n', 'return m + n');
        console.log(sum(1, 2));

        它也一樣的順利執(zhí)行了,使用 Function 構(gòu)造器生成的函數(shù),并不會在創(chuàng)建它的上下文中創(chuàng)建閉包,一般在全局作用域中被創(chuàng)建。當(dāng)運行函數(shù)的時候,只能訪問自己的本地變量和全局變量,不能訪問 Function 構(gòu)造器被調(diào)用生成的上下文的作用域。如同一個站在地上、一個站在一張薄薄的紙上一樣,在這個場景下,幾乎沒有高下之分。

        結(jié)合 ES6 的新特性 Proxy 便能更安全一些

        function evalute(code,sandbox) {
         sandbox = sandbox || Object.create(null);
         const fn = new Function('sandbox', `with(sandbox){return ($[code])}`);
         const proxy = new Proxy(sandbox, {
         has(target, key) {
         // 讓動態(tài)執(zhí)行的代碼認(rèn)為屬性已存在
         return true; 
         }
         });
         return fn(proxy);
        }
        evalute('1+2') // 3
        evalute('console.log(1)') // Cannot read property 'log' of undefined

        我們知道無論 eval 還是 function,執(zhí)行時都會把作用域一層一層向上查找,如果找不到會一直到 global,那么利用 Proxy 的原理就是,讓執(zhí)行了代碼在 sandobx 中找的到,以達到「防逃逸」的目的。

        在瀏覽器中,還可以利用 iframe,創(chuàng)建一個再發(fā)安全的一些隔離環(huán)境,本文也著眼于 Node.js,在這里不做過多討論。
        在 Node.js 中呢,有沒有其它選擇?

        或許沒看到這兒之前你就已經(jīng)想到了 VM,它是 Node.js 默認(rèn)就提供的一個內(nèi)建模塊,VM 模塊提供了一系列 API 用于在 V8 虛擬機環(huán)境中編譯和運行代碼。JavaScript 代碼可以被編譯并立即運行,或編譯、保存然后再運行。

        const vm = require('vm');
        const script = new vm.Script('m + n');
        const sandbox = { m: 1, n: 2 };
        const context = new vm.createContext(sandbox);
        script.runInContext(context);

        執(zhí)行上這的代碼就能拿到結(jié)果 3,同時,通過 vm.Script 還能指定代碼執(zhí)行了「最大毫秒數(shù)」,超過指定的時長將終止執(zhí)行并拋出一個異常

        try {
         const script = new vm.Script('while(true){}',{ timeout: 50 });
         ....
        } catch (err){
         //打印超時的 log
         console.log(err.message);
        }

        上面的腳本執(zhí)行將會失敗,被檢測到超時并拋出異常,然后被 Try Cache 捕獲到并打出 log,但同時需要注意的是 vm.Script 的 timeout 選項「只針對同步代有效」,而不包括是異步調(diào)用的時間,比如

        const script = new vm.Script('setTimeout(()=>{},2000)',{ timeout: 50 });
         ....

        上述代碼,并不是會在 50ms 后拋出異常,因為 50ms 上邊的代碼同步執(zhí)行肯定完了,而 setTimeout 所用的時間并不算在內(nèi),也就是說 vm 模塊沒有辦法對異步代碼直接限制執(zhí)行時間。我們也不能額外通過一個 timer 去檢查超時,因為檢查了執(zhí)行中的 vm 也沒有方法去中止掉。

        另外,在 Node.js 通過 vm.runInContext 看起來似乎隔離了代碼執(zhí)行環(huán)境,但實際上卻很容易「逃逸」出去。

        const vm = require('vm');
        const sandbox = {};
        const script = new vm.Script('this.constructor.constructor("return process")().exit()');
        const context = vm.createContext(sandbox);
        script.runInContext(context);

        執(zhí)行上邊的代碼,宿主程序立即就會「退出」,sandbox 是在 VM 之外的環(huán)境創(chuàng)建的,需 VM 中的代碼的 this 指向的也是 sandbox,那么

        //this.constructor 就是外所的 Object 構(gòu)建函數(shù)
        const ObjConstructor = this.constructor; 
        //ObjConstructor 的 constructor 就是外包的 Function
        const Function = ObjConstructor.constructor;
        //創(chuàng)建一個函數(shù),并執(zhí)行它,返回全局 process 全局對象
        const process = (new Function('return process'))(); 
        //退出當(dāng)前進程
        process.exit();

        沒有人愿意用戶一段腳本就能讓應(yīng)用掛掉吧。除了退出進程序之外,實際上還能干更多的事情。

        有個簡單的方法就能避免通過 this.constructor 拿到 process,如下:

        const vm = require('vm');
        //創(chuàng)建一外無 proto 的空白對象作為 sandbox
        const sandbox = Object.create(null);
        const script = new vm.Script('...');
        const context = vm.createContext(sandbox);
        script.runInContext(context);

        但還是有風(fēng)險的,由于 JavaScript 本身的動態(tài)的特點,各種黑魔法防不勝防。事實 Node.js 的官方文檔中也提到 VM 當(dāng)做一個安全的沙箱去執(zhí)行任意非信任的代碼。

        有哪些做了進一步工作的社區(qū)模塊?

        在社區(qū)中有一些開源的模塊用于運行不信任代碼,例如 sandbox、vm2、jailed 等。相比較而言 vm2 對各方面做了更多的安全工作,相對安全些。

        從 vm2 的官方 READM 中可以看到,它基于 Node.js 內(nèi)建的 VM 模塊,來建立基礎(chǔ)的沙箱環(huán)境,然后同時使用上了文介紹過的 ES6 的 Proxy 技術(shù)來防止沙箱腳本逃逸。

        用同樣的測試代碼來試試 vm2

        const { VM } = require('vm2');
        new VM().run('this.constructor.constructor("return process")().exit()');

        如上代碼,并沒有成功結(jié)束掉宿主程序,vm2 官方 REAME 中說「vm2 是一個沙盒,可以在 Node.js 中按全的執(zhí)行不受信任的代碼」。

        然而,事實上我們還是可以干一些「壞」事情,比如:

        const { VM } = require('vm2');
        const vm = new VM({ timeout: 1000, sandbox: {}});
        vm.run('new Promise(()=>{})');

        上邊的代碼將永遠不會執(zhí)行結(jié)束,如同 Node.js 內(nèi)建模塊一樣 vm2 的 timeout 對異步操作是無效的。同時,vm2 也不能額外通過一個 timer 去檢查超時,因為它也沒有辦法將執(zhí)行中的 vm 終止掉。這會一點點耗費完服務(wù)器的資源,讓你的應(yīng)用掛掉。

        那么或許你會想,我們能不能在上邊的 sandbox 中放一個假的 Promise 從而禁掉 Promise 呢?答案是能提供一個「假」的 Promise,但卻沒有辦法完成禁掉 Promise,比如

        const { VM } = require('vm2');
        const vm = new VM({ 
         timeout: 1000, sandbox: { Promise: function(){}}
        });
        vm.run('Promise = (async function(){})().constructor;new Promise(()=>{});');

        可以看到通過一行 Promise = (async function(){})().constructor 就可以輕松再次拿到 Promise 了。從另一個層面來看,況且或許有時我們還想讓自定義腳本支持異步處理呢。

        如何建立一個更安全一些的沙箱?

        通過上文的探究,我們并沒有找到一個完美的方案在 Node.js 建立安全的隔離的沙箱。其中 vm2 做了不少處理,相對來講算是較安全的方案了,但問題也很明顯,比如異步不能檢查超時的問題、和宿主程序在相同進程的問題。

        沒有進程隔離時,通過 VM 創(chuàng)建的 sanbox 大體是這樣的

        那么,我們是不是可以嘗試,將非受信代碼,通過 vm2 這個模塊隔離在一個獨立的進程中執(zhí)行呢?然后,執(zhí)行超時時,直接將隔離的進程干掉,但這里我們需要考慮如下幾個問題

        通過進程池統(tǒng)調(diào)度管理沙箱進程

        如果來一個執(zhí)行任務(wù),創(chuàng)建一個進程,用完銷毀,僅處理進程的開銷就已經(jīng)稍大了,并且也不能不設(shè)限的開新進程和宿主應(yīng)用搶資源,那么,需要建一個進程池,所有任務(wù)到來會創(chuàng)建一個 Script 實例,先進入一個 pending 隊列,然后直接將 script 實例的 defer 對象返回,調(diào)用處就能 await 執(zhí)行結(jié)果了,然后由 sandbox master 根據(jù)工程進程的空閑程序來調(diào)度執(zhí)行,master 會將 script 的執(zhí)行信息,包括重要的 ScriptId,發(fā)送給空閑的 worker,worker 執(zhí)行完成后會將「結(jié)果 + script 信息」回傳給 master,master 通過 ScriptId 識別是哪個腳本執(zhí)行完畢了,就是結(jié)果進行 resolve 或 reject 處理。

        這樣,通過「進程池」即能降低「進程來回創(chuàng)建和銷毀的開銷」,也能確保不過度搶占宿主資源,同時,在異步操作超時,還能將工程進程直接殺掉,同時,master 將發(fā)現(xiàn)一個工程進程掛掉,會立即創(chuàng)建替補進程。

        處理的數(shù)據(jù)和結(jié)果,還有公開給沙箱的方法

        進程間如何通訊,需要「動態(tài)代碼」處理數(shù)據(jù)可以直接序列化后通過 IPC 發(fā)送給隔離 Sandbox 進程,執(zhí)行結(jié)果一樣經(jīng)過序列化通過 IPC 傳輸。

        其中,如果想法公開一個方法給 sandbox,因為不在一個進程,并不能方便的將一個方案的引用傳遞給 sandbox。我們可以將宿主的方法,在傳遞給 sandbox worker 之類做一下處理,轉(zhuǎn)換為一個「描述對象」,包括了允許 sandbox 調(diào)用的方法信息,然后將信息,如同其它數(shù)據(jù)一樣發(fā)送給 worker 進程,worker 收到數(shù)據(jù)后,識出來所「方法描述對象」,然后在 worker 進程中的 sandbox 對象上建立代理方法,代理方法同樣通過 IPC 和 master 通訊。

        最終,我們建立了一個大約這樣的「沙箱環(huán)境」

        如此這般處理起來是不是感覺很麻煩?但我們就有了一個更加安全一些的沙箱環(huán)境了,這些處理。筆者已經(jīng)基于 TypeScript 編寫,并封裝為一個獨立的模塊 Safeify。

        GitHub: https://github.com/Houfeng/safeify,歡迎 Star & Issues

        最后,簡單介紹一下 Safeify 如何使用,通過如下命令安裝

        npm i safeify --save

        在應(yīng)用中使用,還是比較簡單的,如下代碼(TypeScript 中類似)

        import { Safeify } from './Safeify';
        const safeVm = new Safeify({
         timeout: 50, //超時時間,默認(rèn) 50ms
         asyncTimeout: 500, //包含異步操作的超時時間,默認(rèn) 500ms
         quantity: 4 //沙箱進程數(shù)量,默認(rèn)同 CPU 核數(shù)
        });
        const context = {
         a: 1, 
         b: 2,
         add(a, b) {
         return a + b;
         }
        };
        const rs = await safeVm.run(`return add(a,b)`, context);
        console.log('result',rs);

        相信看了本文案例你已經(jīng)掌握了方法,更多精彩請關(guān)注Gxl網(wǎng)其它相關(guān)文章!

        推薦閱讀:

        ajax怎樣動態(tài)增加表格的tr與td

        react+antd做一個后臺管理系統(tǒng)

        聲明:本網(wǎng)頁內(nèi)容旨在傳播知識,若有侵權(quán)等問題請及時與本網(wǎng)聯(lián)系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com

        文檔

        應(yīng)用沙箱環(huán)境實戰(zhàn)使用

        應(yīng)用沙箱環(huán)境實戰(zhàn)使用:這次給大家?guī)響?yīng)用沙箱環(huán)境實戰(zhàn)使用,應(yīng)用沙箱環(huán)境實戰(zhàn)使用的注意事項有哪些,下面就是實戰(zhàn)案例,一起來看一下。有哪些動態(tài)執(zhí)行腳本的場景?在一些應(yīng)用中,我們希望給用戶提供插入自定義邏輯的能力,比如 Microsoft 的 Office 中的 VBA,比如一些游戲中的
        推薦度:
        • 熱門焦點

        最新推薦

        猜你喜歡

        熱門推薦

        專題
        Top
        主站蜘蛛池模板: 亚洲va久久久噜噜噜久久男同| 亚洲国产美女精品久久久| 亚洲无线一二三四区| 99久久国产精品免费一区二区| 亚洲亚洲人成综合网络| 久久国产亚洲精品| 最新国产乱人伦偷精品免费网站| 久久久久亚洲精品中文字幕| 亚洲午夜久久久久久尤物| 日韩在线免费视频| 亚洲一区二区三区国产精华液| 国产福利免费在线观看| 亚洲明星合成图综合区在线| 免费大片黄在线观看yw| 亚洲综合色一区二区三区| 国产网站免费观看| sss日本免费完整版在线观看| 中文字幕亚洲乱码熟女一区二区| 日本三级在线观看免费| 亚洲综合网美国十次| 中文字幕视频在线免费观看| 国产成人免费福利网站| 国产三级在线免费观看| 亚洲v高清理论电影| 十八禁在线观看视频播放免费| 亚洲av日韩av高潮潮喷无码| 69xx免费观看视频| 亚洲国产高清在线| 亚洲成在人线aⅴ免费毛片| 国产精品亚洲AV三区| 亚洲日本va在线视频观看| 色片在线免费观看| 亚洲精品电影天堂网| 在线播放高清国语自产拍免费| 男人免费视频一区二区在线观看| 日韩va亚洲va欧洲va国产| 国产成人精品免费视频大全麻豆| 麻豆亚洲AV成人无码久久精品| 浮力影院亚洲国产第一页| 久久久久av无码免费网| 日韩免费码中文在线观看|