WebAssembly 安全的現(xiàn)在和未來
正如我們 最近解釋的,WebAssembly 是一種用于以任何語言編寫的二進(jìn)制格式的軟件,旨在最終無需更改就能在任意平臺運(yùn)行。WebAssembly 的第一個應(yīng)用是在 Web 瀏覽器中,以使網(wǎng)站更快、更具交互性。WebAssembly 有計劃推向 Web 之外,從各種服務(wù)器到物聯(lián)網(wǎng)(IoT),其創(chuàng)造了很多機(jī)會,但也存在很多安全問題。這篇文章是對這些問題和 WebAssembly 安全模型的一篇介紹性概述。
WebAssembly 跟 JavaScript 很像
在 Web 瀏覽器內(nèi)部,WebAssembly 模塊由執(zhí)行 JavaScript 代碼的同一 虛擬機(jī)VM 管理。因此,WebAssembly 和 JavaScript 一樣,造成的危害也是相同的,只是效率更高,更不易被察覺。由于 JavaScript 是純文本,運(yùn)行前需要瀏覽器編譯,而 WebAssembly 是一種可立即運(yùn)行的二進(jìn)制格式,運(yùn)行速度更快,也更難被掃描出(即使使用殺毒軟件)其中的惡意指令。
WebAssembly 的這種 “代碼混淆” 效果已經(jīng)被用來彈出不請自來的廣告,或打開假的 “技術(shù)支持” 窗口,要求提供敏感數(shù)據(jù)。另一個把戲則是自動將瀏覽器重定向到包含真正危險的惡意軟件的 “落地” 頁。
最后,就像 JavaScript 一樣,WebAssembly 可能被用來 “竊取” 處理能力而不是數(shù)據(jù)。2019 年,對 150 個不同的 WASM 模塊的分析 發(fā)現(xiàn),其中約 32% 被用于加密貨幣挖掘。
WebAssembly 沙盒和接口
WebAssembly 代碼在一個由虛擬機(jī)(而不是操作系統(tǒng))管理的 沙盒 中封閉運(yùn)行。這使它無法看到主機(jī),也無法直接與主機(jī)交互。對系統(tǒng)資源(文件、硬件或互聯(lián)網(wǎng)連接)的訪問只能通過該虛擬機(jī)提供的 WebAssembly 系統(tǒng)接口WebAssembly System Interface(WASI) 進(jìn)行。
WASI 不同于大多數(shù)其他應(yīng)用程序編程接口(API),它具有獨(dú)特的安全特性,真正推動了 WASM 在傳統(tǒng)服務(wù)器和邊緣Edge計算場景中的采用,這將是下一篇文章的主題。在這里,可以說,當(dāng)從 Web 遷移到其他環(huán)境時,它的安全影響會有很大的不同?,F(xiàn)代 Web 瀏覽器是極其復(fù)雜的軟件,但它是建立在數(shù)十年的經(jīng)驗(yàn)和數(shù)十億人的日常測試之上的。與瀏覽器相比,服務(wù)器或物聯(lián)網(wǎng)(IoT)設(shè)備幾乎是未知領(lǐng)域。這些平臺的虛擬機(jī)將需要擴(kuò)展 WASI,因此,肯定會帶來新的安全挑戰(zhàn)。
WebAssembly 中的內(nèi)存和代碼管理
與普通的編譯程序相比,WebAssembly 應(yīng)用程序?qū)?nèi)存的訪問非常受限,對它們自己也是如此。WebAssembly 代碼不能直接訪問尚未調(diào)用的函數(shù)或變量,不能跳轉(zhuǎn)到任意地址,也不能將內(nèi)存中的數(shù)據(jù)作為字節(jié)碼指令執(zhí)行。
在瀏覽器內(nèi)部,WASM 模塊只能獲得一個連續(xù)字節(jié)的全局?jǐn)?shù)組(線性內(nèi)存linear memory)進(jìn)行操作。WebAssembly 可以直接讀寫該區(qū)域中的任意位置,或者請求增加其大小,但僅此而已。這個線性內(nèi)存linear memory也與包含其實(shí)際代碼、執(zhí)行堆棧、當(dāng)然還有運(yùn)行 WebAssembly 的虛擬機(jī)的區(qū)域分離。對于瀏覽器來說,所有這些數(shù)據(jù)結(jié)構(gòu)都是普通的 JavaScript 對象,使用標(biāo)準(zhǔn)過程與所有其他對象隔離。
結(jié)果還好,但不完美
所有這些限制使得 WebAssembly 模塊很難做出不當(dāng)行為,但也并非不可能。
沙盒化的內(nèi)存使 WebAssembly 幾乎不可能接觸到 外部 的東西,也使操作系統(tǒng)更難防止 內(nèi)部 發(fā)生不好的事情。傳統(tǒng)的內(nèi)存監(jiān)測機(jī)制,比如 堆棧金絲雀Stack Canaries 能注意到是否有代碼試圖擾亂它不應(yīng)該接觸的對象,但在這里沒用。
事實(shí)上,WebAssembly 只能訪問自己的線性內(nèi)存linear memory,但可以直接訪問,這也可能為攻擊者的行為 提供便利。有了這些約束和對模塊源代碼的訪問,就更容易猜測覆蓋哪些內(nèi)存位置可能造成最大的破壞。破壞局部變量似乎也是 可能的,因?yàn)樗鼈兺A粼诰€性內(nèi)存linear memory中的無監(jiān)督堆棧中。
2020 年的一篇關(guān)于 WebAssembly 的二進(jìn)制安全性 的論文指出,WebAssembly 代碼仍然可以在設(shè)定的常量內(nèi)存中覆蓋字符串文字。同一篇論文描述了在三個不同的平臺(瀏覽器、Node.JS 上的服務(wù)端應(yīng)用程序,和獨(dú)立 WebAssembly 虛擬機(jī)的應(yīng)用程序)上,WebAssembly 可能比編譯為原生二進(jìn)制文件時更不安全的其他方式。建議進(jìn)一步閱讀此主題。
通常,認(rèn)為 WebAssembly 只能破壞其自身沙盒中的內(nèi)容的想法可能會產(chǎn)生誤導(dǎo)。WebAssembly 模塊為調(diào)用它們的 JavaScript 代碼做繁重的工作,每次都會交換變量。如果模塊在這些變量中的任意一處寫入不安全的調(diào)用 WebAssembly 的 JavaScript 代碼,就 會 導(dǎo)致崩潰或數(shù)據(jù)泄露。
未來的方向
WebAssembly 的兩個新出現(xiàn)的特性:并發(fā) 和內(nèi)部垃圾收集,肯定會影響其安全性(如何影響以及影響多少,現(xiàn)在下結(jié)論還為時過早)。
并發(fā)允許多個 WebAssembly 模塊在同一個虛擬機(jī)中并行。目前,只有通過 JavaScript web workers 才能實(shí)現(xiàn)這一點(diǎn),但更好的機(jī)制正在開發(fā)中。安全方面,他們可能會帶來 以前不需要的大量的代碼,也就是更多出錯的方法。
為了提高性能和安全性,我們需要一個 本地的垃圾收集器,但最重要的是,要在經(jīng)過良好測試的瀏覽器的 Java 虛擬機(jī)之外使用 WebAssembly,因?yàn)檫@些虛擬機(jī)無論如何都會在自己內(nèi)部收集所有的垃圾。當(dāng)然,甚至這個新代碼也可能成為漏洞和攻擊的另一個入口。
往好處想,使 WebAssembly 比現(xiàn)在更安全的通用策略也是存在的。這些策略包括:編譯器改進(jìn)、棧/堆和常量數(shù)據(jù)的 分離 的線性存儲機(jī)制,以及避免使用 不安全的語言(如 C)編譯 WebAssembly 模塊代碼。