NPM酷庫,天天兩分鐘,瞭解一個流行NPM庫。git
今天咱們要了解的庫是 vm2,則是一個Node.js 官方 vm 庫的替代品,主要解決了安全問題。github
在Node.js官方標準庫中有一個vm庫,用來在V8虛擬機環境中編譯執行JS代碼。一般,咱們用vm庫來實現一個沙箱,在代碼主程序以外執行額外的JS腳本。安全
有時,咱們須要vm虛擬機來執行不受信任的代碼,這些代碼多是由用戶提交的,好比在脈衝雲接口文檔管理中,容許用戶提交Mock.js腳本生成模擬接口數據。而Node.js標準庫中的vm是不安全的,用戶腳本能夠輕易突破沙箱環境,獲取主程序的Context!閉包
const vm = require('vm'); vm.runInNewContext('this.constructor.constructor("return process")().exit()'); console.log('Never gets executed.');
上述代碼在執行時,程序在第二行就直接退出,vm虛擬機環境中的代碼逃逸,得到了主線程的 process 變量,並調用 process.exit(),形成主程序非正常退出。函數
上文中的代碼使用了runInNewContext函數簡寫,等價於以下代碼:ui
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); console.log('Never gets executed.');
從代碼中得知,建立vm環境時,首先要初始化一個對象 sendbox,這個對象就是vm中腳本執行時的全局環境Context,vm 腳本中全局 this 指向的就是這個對象。this
而vm中腳本等同於:spa
const sandbox = this; // 獲取Context const ObjectConstructor = this.constructor; // 獲取 Object 對象構造函數 const FunctionConstructor = ObjectConstructor.constructor; // 獲取 Function 對象構造函數 const myfun = FunctionConstructor('return process'); // 構造一個函數,返回process全局變量 const process = myfun(); process.exit();
從上邊腳本中能夠看出vm不安全的緣由。vm內部腳本的Context對象是在主程序中定義的,根據JS原型鏈原理,能夠輕鬆獲取主程序中的 Function 對象,用主程序的 Function 對象構造一個函數,那麼這個函數運行時,就是在主程序閉包環境中執行的!因此,咱們輕易地獲取到了主程序的全局對象 process,最終控制主程序!線程
vm2就是專門爲了解決vm的安全問題而誕生的。code
const { VM } = require('vm2'); const vm = new VM({ timeout: 1000, sandbox: {} }); vm.run(`process.exit()`); // TypeError: process.exit is not a function
while (true) {}
首先,vm2基於vm,使用官方的vm庫構建沙箱環境。而後使用JavaScript的Proxy技術來防止沙箱腳本逃逸。
VM2 https://github.com/patriksime...
Proxy https://developer.mozilla.org...
天天瞭解一個NPM庫,一年後成爲Node.js高手