亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 開發 > JS > 正文

Node.js應用設置安全的沙箱環境

2024-05-06 16:43:53
字體:
來源:轉載
供稿:網友

有哪些動態執行腳本的場景?

在一些應用中,我們希望給用戶提供插入自定義邏輯的能力,比如 Microsoft 的 Office 中的 VBA,比如一些游戲中的 lua 腳本,FireFox 的「油猴腳本」,能夠讓用戶發在可控的范圍和權限內發揮想象做一些好玩、有用的事情,擴展了能力,滿足用戶的個性化需求。

大多數都是一些客戶端程序,在一些在線的系統和產品中也常常也有類似的需求,事實上,在線的應用中也有不少提供了自定義腳本的能力,比如 Google Docs 中的 Apps Script,它可以讓你使用 JavaScript 做一些非常有用的事情,比如運行代碼來響應文檔打開事件或單元格更改事件,為公式制作自定義電子表格函數等等。

與運行在「用戶電腦中」的客戶端應用不同,用戶的自定義腳本通常只能影響用戶自已,而對于在線的應用或服務來講,有一些情況就變得更為重要,比如「安全」,用戶的「自定義腳本」必須嚴格受到限制和隔離,即不能影響到宿主程序,也不能影響到其它用戶。

而 Safeify 就是一個針對 Nodejs 應用,用于安全執行用戶自定義的非信任腳本的模塊。

怎樣安全的執行動態腳本?

我們先看看通常都能如何在 JavaScript 程序中動態執行一段代碼?比如大名頂頂的 eval

eval('1+2')

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

再來看看 Functon,通過 Function 構造器,我們可以動態的創建一個函數,然后執行它

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

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

結合 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) {  // 讓動態執行的代碼認為屬性已存在  return true;  } }); return fn(proxy);}evalute('1+2') // 3evalute('console.log(1)') // Cannot read property 'log' of undefined

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

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

或許沒看到這兒之前你就已經想到了 VM,它是 Node.js 默認就提供的一個內建模塊,VM 模塊提供了一系列 API 用于在 V8 虛擬機環境中編譯和運行代碼。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);

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

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

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

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

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

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

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);

執行上邊的代碼,宿主程序立即就會「退出」,sandbox 是在 VM 之外的環境創建的,需 VM 中的代碼的 this 指向的也是 sandbox,那么

//this.constructor 就是外所的 Object 構建函數const ObjConstructor = this.constructor; //ObjConstructor 的 constructor 就是外包的 Functionconst Function = ObjConstructor.constructor;//創建一個函數,并執行它,返回全局 process 全局對象const process = (new Function('return process'))(); //退出當前進程process.exit(); 

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

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

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

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

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

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

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

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

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

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

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

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

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

那么或許你會想,我們能不能在上邊的 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 創建的 sanbox 大體是這樣的

Node.js,沙箱

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

通過進程池統調度管理沙箱進程

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

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

處理的數據和結果,還有公開給沙箱的方法

進程間如何通訊,需要「動態代碼」處理數據可以直接序列化后通過 IPC 發送給隔離 Sandbox 進程,執行結果一樣經過序列化通過 IPC 傳輸。

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

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

Node.js,沙箱

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

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

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

npm i safeify --save

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

import { Safeify } from './Safeify';const safeVm = new Safeify({ timeout: 50,     //超時時間,默認 50ms asyncTimeout: 500,  //包含異步操作的超時時間,默認 500ms quantity: 4      //沙箱進程數量,默認同 CPU 核數});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);

關于安全的問題,沒有最安全,只有更安全,Safeify 已在一個項目中使用,但自定義腳本的功能是僅針對內網用戶,有不少動態執行代碼的場景其實是可以避免的,繞不開或實在需要提供這個功能時,希望本文或 Safeify 能對大家有所幫助就行了。


注:相關教程知識閱讀請移步到JavaScript/Ajax教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
亚洲精品美女久久久| 911国产网站尤物在线观看| 精品久久国产精品| 欧美午夜无遮挡| 成人免费视频a| 亚洲激情国产精品| 中文字幕亚洲欧美在线| 欧美高清视频一区二区| 亚洲欧美在线免费观看| 国内外成人免费激情在线视频网站| 久久久久成人精品| 久久久中精品2020中文| 国产欧美在线播放| 91精品久久久久| 亚洲图片欧洲图片av| 久久影视电视剧免费网站清宫辞电视| 深夜福利91大全| 欧美国产日韩一区二区在线观看| 97成人精品视频在线观看| 欧美在线一级视频| 精品视频在线播放| 亚洲黄色www网站| 欧美性猛交99久久久久99按摩| 欧美激情影音先锋| 日韩成人中文电影| 97精品国产97久久久久久春色| 97国产精品人人爽人人做| 欧美一区三区三区高中清蜜桃| 亚洲天堂一区二区三区| 久久精品国产96久久久香蕉| 黄网动漫久久久| 久久精品99国产精品酒店日本| 欧美性资源免费| 日韩在线视频导航| 成人精品一区二区三区电影免费| 欧美性xxxxx极品| 亚洲直播在线一区| 成人精品久久久| 韩国国内大量揄拍精品视频| 亚洲三级av在线| 91爱视频在线| 亚洲免费视频一区二区| 国产美女精品免费电影| 欧美日韩免费网站| 久久久久久久久久久亚洲| 久久久精品一区二区三区| 亚洲va久久久噜噜噜久久天堂| 欧美在线一区二区视频| 欧美日韩中文在线| 欧美性猛交xxxx免费看久久久| 国产精品人成电影| 欧美性猛交xxxx| 欧美精品免费在线| 欧美成aaa人片在线观看蜜臀| 91沈先生在线观看| 欧美俄罗斯性视频| 国产精品久久久久av免费| www.色综合| 国内精品国产三级国产在线专| 色妞欧美日韩在线| 美女久久久久久久久久久| 久久久久久九九九| 亚洲色图av在线| 91性高湖久久久久久久久_久久99| 久久久久久久久久久久久久久久久久av| 国产情人节一区| 成人淫片在线看| 91九色蝌蚪国产| 国产精品久久激情| 亚洲国产中文字幕久久网| 57pao成人永久免费视频| 欧美精品中文字幕一区| 日韩美女激情视频| 久久久国产精品x99av| 欧美大尺度电影在线观看| 久久久之久亚州精品露出| 日韩av中文字幕在线播放| 日本一区二区三区在线播放| 亚洲成在人线av| 久久久精品999| 欧美高清视频免费观看| 91精品视频免费观看| 欧美大片在线看免费观看| 日韩精品亚洲精品| 午夜精品国产精品大乳美女| 国产91ⅴ在线精品免费观看| 欧美国产在线视频| 国内精品久久久久影院优| 国产精品成人aaaaa网站| 日韩视频第一页| 国产精品免费久久久久久| 91精品视频免费看| 一本色道久久综合狠狠躁篇怎么玩| 在线亚洲午夜片av大片| 一本色道久久88综合亚洲精品ⅰ| 北条麻妃一区二区三区中文字幕| 91免费看国产| 精品无人国产偷自产在线| 亚洲国产精品久久久| 蜜臀久久99精品久久久久久宅男| 成人黄色av网站| 久久在精品线影院精品国产| 奇门遁甲1982国语版免费观看高清| 尤物九九久久国产精品的特点| 日韩最新免费不卡| 精品亚洲一区二区三区| 欧美国产第二页| 日韩经典一区二区三区| 中文字幕在线看视频国产欧美在线看完整| 国产午夜精品免费一区二区三区| 国产精品美女呻吟| 中文字幕综合在线| 在线观看国产精品日韩av| 国产精品第1页| 国产亚洲精品va在线观看| 国产免费一区二区三区香蕉精| 国产精品91在线| 国产精品视频内| 欧美性猛交xxxx富婆弯腰| 茄子视频成人在线| 亚洲在线观看视频| 亚洲第一精品夜夜躁人人躁| 亚洲人午夜精品免费| www.欧美精品一二三区| 另类色图亚洲色图| 欧美日韩成人精品| 成人福利免费观看| 中文字幕一精品亚洲无线一区| 日韩在线观看网址| 91国内揄拍国内精品对白| 国产视频福利一区| 欧美性猛交xxxx免费看久久久| 91麻豆国产语对白在线观看| 亚洲淫片在线视频| 亚洲黄色www网站| 精品国产鲁一鲁一区二区张丽| 国产aⅴ夜夜欢一区二区三区| 91久久综合亚洲鲁鲁五月天| 国产亚洲视频在线| 国产精品91久久久| 国产精品久久久久aaaa九色| 国产精品自拍偷拍视频| 国产成人精品在线播放| 欧美成人在线免费| 久久偷看各类女兵18女厕嘘嘘| 欧美黑人巨大xxx极品| 亚洲欧美另类自拍| 91精品久久久久久久久不口人| 国产suv精品一区二区| 亚洲成人激情图| 国产日韩欧美黄色| 九九热视频这里只有精品| 国外日韩电影在线观看| 黑人精品xxx一区一二区| 日韩激情av在线播放| 国产视频精品在线| 91深夜福利视频| 色悠悠久久久久| 不卡中文字幕av| 欧洲成人在线观看| 欧美大人香蕉在线| 国产精品视频一区二区高潮| 国产精品成人一区二区三区吃奶| 国内成人精品视频|