一、沙箱逃逸概念
- JavaScript和Nodejs之間有什么區別:JavaScript用在瀏覽器前端,后來將Chrome中的v8引擎單獨拿出來為JavaScript單獨開發了一個運行環境,因此JavaScript也可以作為一門后端語言,寫在后端(服務端)的JavaScript就叫叫做Nodejs。
- 什么是沙箱(sandbox)當我們運行一些可能會產生危害的程序,我們不能直接在主機的真實環境上進行測試,所以可以通過單獨開辟一個運行代碼的環境,它與主機相互隔離,但使用主機的硬件資源,我們將有危害的代碼在沙箱中運行只會對沙箱內部產生一些影響,而不會影響到主機上的功能,沙箱的工作機制主要是依靠重定向,將惡意代碼的執行目標重定向到沙箱內部。
- 沙箱(sandbox)和 虛擬機(VM)和 容器(Docker)之間的區別:sandbox和VM使用的都是虛擬化技術,但二者間使用的目的不一樣。沙箱用來隔離有害程序,而虛擬機則實現了我們在一臺電腦上使用多個操作系統的功能。Docker屬于sandbox的一種,通過創造一個有邊界的運行環境將程序放在里面,使程序被邊界困住,從而使程序與程序,程序與主機之間相互隔離開。在實際防護時,使用Docker和sandbox嵌套的方式更多一點,安全性也更高。
- 在Nodejs中,我們可以通過引入vm模塊來創建一個“沙箱”,但其實這個vm模塊的隔離功能并不完善,還有很多缺陷,因此Node后續升級了vm,也就是現在的vm2沙箱,vm2引用了vm模塊的功能,并在其基礎上做了一些優化。
二、Node將字符串執行為代碼
方法一 eval
首先我在目錄下創建一個age.txt
var age = 18
創建一個y1.js
const fs = require('fs')let content = fs.readFileSync('age.txt', 'utf-8')console.log(content)eval(content)console.log(age)
這里因為我沒有配置Node.js,所以我將代碼改為.txt在瀏覽器中為大家調試(結果一樣)
fetch('age.txt').then(response => response.text()).then(content => {console.log(content);eval(content);console.log(age);}).catch(error => console.error('An error occurred:', error));
?結果:
?可以發現我們通過eval執行了一個字符串,但是這種執行方式如果在當前作用域下已經有了同名的age變量,這個程序就會報錯。
?相同變量程序報錯:
?在js中每一個模塊都有自己獨立的作用域,所以用eval執行字符串代碼很容易出現上面的這個問題,我們再看另外一種方法。
方法二:new Function
上面的方法因為模塊間的作用域被限制了使用,那么我們考慮一下如果能夠自己創建一個作用域是不是就可以更加方便的執行代碼呢?new Function的第一個參數是形參名稱,第二個參數是函數體。
我們都知道函數內和函數外是兩個作用域,不過當在函數中的作用域想要使用函數外的變量時,要通過形參來傳遞,當參數過多時這種方法就變的麻煩起來了。
從上面兩個執行代碼的例子可以看出來其實我們的思想就是如何創建一個能夠通過傳一個字符串就能執行代碼,并且還與外部隔絕的作用域,這也就是vm模塊的作用。
三、Nodejs作用域
說到作用域,我們就要說一下Node中的作用域是怎么分配的(在Node中一般把作用域叫上下文)。
在Web端(瀏覽器),發揮作用的一般是JavaScript,學過JavaScript的師傅應該都知道我們打開瀏覽器的窗口是JavaScript中最大的對象window
,那么在服務端發揮作用的Node它的構造和JavaScript不太一樣。
我們在寫一個Node項目時往往要在一個文件里ruquire其他的js文件,這些文件我們都給它們叫做“包”。每一個包都有一個自己的上下文,包之間的作用域是互相隔離不互通的,也就是說就算我在y1.js中require了y2.js,那么我在y1.js中也無法直接調用y2.js中的變量和函數,舉個例子。
在同一級目錄下有y1.js
和y2.js
兩個文件
y1.js:
var age = 20
y2.js:
const a = require("./y1")console.log(a.age)
運行y2.js發現報錯:
那么我們想y2中引入并使用y1中的元素應該怎么辦呢,Node給我們提供了一個將js文件中元素輸出的接口exports
?,把y1修改成下面這樣:
y1.js:
var age = 20exports.age = age
我們再運行y2就可以拿到age的值了
圖解:
?這個時候就有人會問左上角的global是什么?這里就要說到Nodejs中的全局對象了。
剛才我們提到在JavaScript中window
是全局對象,瀏覽器其他所有的屬性都掛載在window
下,那么在服務端的Nodejs中和window
類似的全局對象叫做global
,Nodejs下其他的所有屬性和包都掛載在這個global對象下。在global下掛載了一些全局變量,我們在訪問這些全局變量時不需要用global.xxx
的方式來訪問,直接用xxx
就可以調用這個變量。舉個例子,console
就是掛載在global下的一個全局變量,我們在用console.log
輸出時并不需要寫成global.console.log
,其他常見全局變量還有process(一會逃逸要用到)。
我們也可以手動聲明一個全局變量,但全局變量在每個包中都是共享的,所以盡量不要聲明全局變量,不然容易導致變量污染。用上面的代碼舉個例子: