“請問我怎么才能保證 Java 程序內(nèi)存中密碼的安全呢?”如果你也過有類似的問題,并且在網(wǎng)上搜到一些并不十分完善卻又無可奈何的答案,說明你就是 Java 程序安全性問題的 stakeholder。
這個問題的標準答案是 Java 機密計算技術(shù),它將機密計算技術(shù)引入 Java 的世界,為 Java 程序的安全性帶來了重大的提升?;诖?,龍蜥社區(qū)云原生機密計算 SIG 推出了 Java 機密計算的具體實現(xiàn)技術(shù)——Teaclave Java TEE SDK, 以下簡稱 Teaclave Java。該技術(shù)具有以下顯著優(yōu)點:
全場景安全性。當(dāng)用戶有機密計算硬件支持時,Teaclave Java 可以實現(xiàn)最高安全等級的 Java 可信計算;當(dāng)用戶沒有相關(guān)硬件時,退化為 安全沙箱隔離 級別的可信計算,亦可有效保護用戶的敏感數(shù)據(jù)和計算過程的安全性。
開發(fā)和構(gòu)建簡單。基于 SPI 的純 Java 編程模型,一鍵式構(gòu)建,將 Java 機密計算開發(fā)構(gòu)建門檻一降到底。
Teaclave Java 已經(jīng)過企業(yè)級內(nèi)部場景的驗證,在 Apache 社區(qū)開源。描述本技術(shù)的論文由龍蜥社區(qū)云原生機密計算 SIG 與上海交通大學(xué)、大連理工大學(xué)合作發(fā)表在軟件工程頂會 ICSE 2023(https://conf.researchr.org/home/icse-2023)上,并且獲得了本屆會議的 ACM SIGSOFT 杰出論文獎。這是 2020 年以來,龍蜥社區(qū)云原生機密計算 SIG、上海交通大學(xué)、大連理工大學(xué)首次獲此殊榮。
問題的本質(zhì)
這個問題的本質(zhì)是如何在具有風(fēng)險的運行時環(huán)境中安全地使用敏感數(shù)據(jù)。當(dāng)我們在運行時將密碼解密后,密碼就會以明文的形式存在于內(nèi)存中,也就是 Java 的堆上,如圖 1 的左半部分所示。如果系統(tǒng)遭受攻擊,比如 2021 年聲名大噪的 log4j 漏洞攻擊,Java 堆上的內(nèi)容就會被竊取。即便沒有被攻擊,在為了性能診斷而做 heap dump 時也有可能主動將敏感信息泄漏出去。所以將諸如密碼這樣的安全敏感信息暴露在普通運行環(huán)境具有高度的風(fēng)險。
(解圖 1 樸素的 Java 密碼保護示意圖)
一種保護思路是盡可能地縮短明文密碼在內(nèi)存中的存放時間,以縮短敏感信息暴露的時間窗口。
如圖 1 右半部分所示,在使用完密碼后,及時將其從內(nèi)存中銷毀,這樣會比先前更加安全一些。因為密碼是文本信息,會用字符串類 java.lang.String 保存。Java 的 String 是一種 immutable 類型,創(chuàng)建后不能更改內(nèi)容,所以沒有可以重置內(nèi)容的 API。要銷毀密碼只能通過反射將 String 類內(nèi)部保存字符內(nèi)容的數(shù)組內(nèi)容置空,從而將密碼內(nèi)容從 Java 堆上抹去。直接將密碼字符串設(shè)置為 null 是沒有用的,這樣只是把 String 變量的指針設(shè)為空,對于 Java 堆上的密碼數(shù)據(jù)沒有任何影響,只有等到下次垃圾回收時才有可能將密碼數(shù)據(jù)從堆中清除。
另一種方法是用 char 數(shù)組保存密碼,而不是 String 類,這樣就不必調(diào)用反射,讓銷毀更加便利。還有一種方法是用 byte 數(shù)組保存密碼,因為其明文是字符編碼而非人可讀的字符,所以會更難被人看懂。
這些就是目前可以從網(wǎng)絡(luò)上搜到的解決方法,本文將它們稱為“樸素”的 Java 密碼保護方案。因為這些方案只是縮短了明文密碼在 Java 堆上的生存時間,并沒有真正將明文密碼保護起來。而且“及時”一詞有很大的彈性,開發(fā)人員未必能準確地判斷出何時才是及時。
更具有典型性的案例就是著名的 log4j 漏洞問題(https://nvd.nist.gov/vuln/detail/CVE-2021-44228)。攻擊者可以利用 log4j 2.14 的漏洞將惡意 class 文件上傳到服務(wù)器并通過 Java 的動態(tài)類加載機制運行,從而竊取 Java 堆中保存的服務(wù)器私鑰。有了私鑰,服務(wù)器與客戶端之間的所有通信內(nèi)容對于攻擊者都如同明文了。
在以上兩個例子中,需要在運行時保護的密碼和密鑰都是安全敏感的數(shù)據(jù)。而在實際場景中,保護范圍并不僅限于敏感數(shù)據(jù),還有可能擴大到運算過程。比如鑒權(quán)認證場景中,需要保證認證的過程可信,不能被攻擊者篡改。再比如云服務(wù)的用戶將自己的算法部署上云時,雖然部署的制品可以加密,以保護傳輸和存儲時的安全,云廠商提供了固若金湯的安全防護以免受外部攻擊,但是用戶依然會擔(dān)心云廠商有沒有在運行時窺探用戶的計算過程,是否存在監(jiān)守自盜的可能。
由此可見,保護 Java 應(yīng)用中的安全敏感數(shù)據(jù)和運算并不是一件遙遠的需求,而是具有迫切的現(xiàn)實意義的需求。對于云計算的供應(yīng)商,讓用戶相信其敏感數(shù)據(jù)和運算對于云廠商自己也是不可見的黑盒亦具有重大的商業(yè)價值。
Java 機密計算現(xiàn)狀
保護運行時的敏感數(shù)據(jù)并不是一個新鮮話題,而屬于迄今已發(fā)展了 20 多年的技術(shù)——機密計算的一部分。機密計算是一種提供硬件級的系統(tǒng)隔離,以保障數(shù)據(jù)安全和程序運行安全的技術(shù)。機密計算將執(zhí)行環(huán)境劃分為富執(zhí)行環(huán)境(Rich Execution Environment,REE)和可信執(zhí)行環(huán)境(Trusted Execution Environment, TEE),認為 REE 和 TEE 應(yīng)該相互隔離,TEE 需要通過硬件加密以保證外界無法知曉其中的內(nèi)容。安全敏感的內(nèi)容應(yīng)該放在 TEE 中運行,其他內(nèi)容則在 REE 中執(zhí)行。
這套機制早在 1999 年就已提出,不過早期的硬件加密技術(shù)能力有限,僅有支持執(zhí)行加解密程序的 TPM(可信任的平臺模塊,Trusted Platform Module)硬件。2008 年 Arm 發(fā)布 TrustZone 技術(shù)白皮書,以支持 Arm 平臺的通用型機密計算任務(wù)。2015 年 Intel 也推出了帶有支持通用應(yīng)用加密的 SGX(軟件保護擴展,Software Guard Extension)芯片的硬件設(shè)備,2021 年 SGX 升級為可支持 1T 內(nèi)存、具有更高性能的 SGX2。
機密計算的核心理念是在具有被攻擊風(fēng)險的運行時環(huán)境中提供一塊安全區(qū)域供安全敏感程序運行,實現(xiàn)了安全敏感數(shù)據(jù)和程序在傳輸、存儲和計算全流程的安全可信。目前機密計算在隱私安全、區(qū)塊鏈、多方計算、IoT 和邊緣設(shè)備,以及個人計算設(shè)備上均有廣泛的應(yīng)用和廣闊的前景。
看起來機密計算技術(shù)正是解決 Java 程序安全性問題的標準答案,那么我們是否能夠在 Java 應(yīng)用中應(yīng)用機密計算技術(shù)呢?
Occlum – 在 TEE 中放入 JVM 和應(yīng)用整體
SGX、TrustZone 等為通用型機密計算提供了硬件基礎(chǔ),Intel、微軟等開源的驅(qū)動和 SDK 則為通用型機密計算提供了軟件基礎(chǔ)?;谶@些軟硬件基礎(chǔ),開發(fā)者已經(jīng)可以在軟件應(yīng)用中使用機密計算。但是機密計算對于 Java 應(yīng)用并不友好,因為 TEE 中只能運行 native 程序,所以 Java 程序并不能直接運行于 TEE 中。要在 TEE 中運行 Java 程序,就必須先在 TEE 中啟動一個 JVM,然后在 JVM 上執(zhí)行 Java 程序。 那么是否能在 TEE 中運行 JVM 呢?答案是肯定的,那就是 Occlum,其原理如圖 2 所示。
Occlum 是介于 TEE 底層 SDK 與 JVM 之間的一層 LibOS,作為操作系統(tǒng)支持普通 JVM 在 TEE 中的運行。 用戶將包含了機密代碼在內(nèi)的整個 Java 程序部署在 TEE 中,由 Occlum 支持 JVM 執(zhí)行。圖 2 右半部分給出了部署的結(jié)構(gòu),其中黃色的 APP 代表整個 Java 應(yīng)用及其所需三方庫,紅色圓圈代表可信代碼。應(yīng)用通過 REE 中的啟動器——通常只是一個很小的命令行工具,啟動執(zhí)行。這種方案的兼容性好,用戶基本不需要修改原有代碼即可獲得機密計算支持。但是缺點也很明顯——放入 TEE 的代碼太多,會導(dǎo)致兩個問題:
安全性下降。 原本需要在 TEE 中執(zhí)行的可信程序可能并不多,但是此方案需要將所有的 Java 程序、三方庫、JVM 和 LibOS 全部放入 TEE,導(dǎo)致 TCB(可信計算基,Trusted Computing Base)太大,安全性并不理想。TCB 是安全領(lǐng)域衡量安全性的重要指標,指信任的代碼量。TCB 越大,其中可能存在安全隱患的代碼就越多,程序的安全性就越差,所以 TCB 越小越好。以 log4j 攻擊為例,Occlum 仍然無法對其免疫。因為 log4j 庫與機密代碼并沒有被分隔在不同的執(zhí)行環(huán)境中,而都部署在 TEE 中,所以攻擊者上傳的惡意類文件也會位于 TEE 中,仍然可以從內(nèi)存中訪問到私鑰。
性能下降。TEE 的硬件不是通用硬件,與 REE 相比存在性能退化,所以將應(yīng)用整體放入 TEE 中會導(dǎo)致整個應(yīng)用的性能下降。但用戶原本的需求只是局部加密,為了局部加密而導(dǎo)致整體性能下降會增大應(yīng)用機密計算的成本。雖然一般用戶可以接受為了安全而產(chǎn)生的部分性能退化,但是對于過度加密產(chǎn)生的額外性能退化會感到難以接受。
圖片(圖 2 Occlum 原理示意圖)
綜上可見,Occlum 方案雖然具有簡單易行的優(yōu)勢,但是其在安全性和性能方面的缺點卻是其投入實際應(yīng)用的主要障礙。
Teaclave Java TEE SDK –? 在 TEE 中僅放入可信代碼
因為在 TEE 中整體支持 JVM 和全部應(yīng)用程序的方案會在 TEE 中執(zhí)行過多的代碼,導(dǎo)致安全性和性能下降而難以投入實用,能不能換種思路,僅將可信代碼放入 TEE 呢?考慮到 TEE 中只能執(zhí)行 native code,那么是不是可以將可信代碼從 Java 代碼直接編譯為 native code 放入 TEE 運行呢?答案是肯定的,這就是本文的主角 Teaclave Java TEE SDK,以下簡稱 Teaclave Java。
Teaclave Java 是由 JVM 團隊開發(fā)的 Java 機密計算開發(fā)框架和構(gòu)建工具鏈,可以一站式快速實現(xiàn) Java 機密計算應(yīng)用的開發(fā)和構(gòu)建。退一步考慮,即使用戶沒有支持機密計算的硬件環(huán)境,Teaclave Java 也可以實現(xiàn)安全沙箱隔離,有效保障敏感數(shù)據(jù)和程序的運行時安全。
Teaclave Java 的關(guān)鍵技術(shù)特性有:
模塊分隔、機密計算服務(wù)化,如圖 3 所示。
簡潔完善的機密計算服務(wù)生命周期管理 API。
Java 靜態(tài)編譯機密內(nèi)容。
隱藏實現(xiàn)細節(jié)、自動生成所有輔助代碼。
在這些技術(shù)的支持下,Teaclave Java 能夠?qū)钠胀K到機密模塊的 Java 模塊間服務(wù)調(diào)用轉(zhuǎn)為從普通模塊到機密 native 庫的函數(shù)調(diào)用,如圖 4 所示。
模塊分隔、機密計算服務(wù)化
Teaclave Java 將應(yīng)用代碼分為三個模塊,Host、Enclave 和 Common。Host 中是普通的安全非敏感程序,Enclave 中是安全敏感程序,Common 中則是前兩者都會用到的公共代碼。這種模塊劃分方式一是為了讓開發(fā)者感知到代碼的安全性區(qū)分,二是為了構(gòu)建時針對不同模塊使用不同工具鏈的便利性。
Host 和 Enclave 是解耦合的,它們之間只能通過 Java 的 SPI(Service Provider Interface)機制交互,而不能直接調(diào)用。機密計算的實現(xiàn)在 Enclave 模塊中被封裝成為了服務(wù),其接口聲明定義在 Common 模塊中,并用 @EnclaveService 注解標識。當(dāng) Host 中的程序需要用到某一機密計算任務(wù)時,就可以先加載服務(wù)實例,再調(diào)用相應(yīng)的函數(shù)。這一結(jié)構(gòu)組織關(guān)系如圖 3 所示。
(圖 3 Teaclave Java 的開發(fā)視圖)
例如我們可以在 Common 中聲明一個如代碼塊 1 所示的機密計算服務(wù)接口,其中提供了用于認證加密的密碼是否有效的 API,authenticate 函數(shù)。該函數(shù)接受一個用戶傳入的加密的密碼,返回該密碼的認證結(jié)果。
代碼塊 1:在 Common 模塊定義機密計算服務(wù)接口聲明示例
@EnclaveService public interface AuthenticationService { /** * Given an encrypted input password, check if it is the correct password. * @param inputPwd the encrypted password to be authenticated * @return true if the given password is correct. */ boolean authenticate(String inputPwd); }
AuthenticationService 接口的具體實現(xiàn)則在 Enclave 模塊的 AuthenticationServiceImpl 類中定義,如代碼塊 2 所示。該類的 authenticate 函數(shù)先使用私鑰對輸入的加密字符串解密,獲得明文結(jié)果,然后將其和內(nèi)存中保存的正確的密碼比對,再返回是否一致的檢查結(jié)果。該類中保存的正確密碼值和私鑰都是安全敏感數(shù)據(jù),authenticate 函數(shù)的實現(xiàn)也是安全敏感運算。它們都將在 TEE 中運行,以黑盒的形式提供給外部使用。從外部只能看到加密的輸入數(shù)據(jù)和返回的判定結(jié)果,而無法窺探到實際的運行過程和數(shù)據(jù)。
代碼塊 2 在 Enclave 模塊定義機密計算服務(wù)接口實現(xiàn)示例
public class AuthenticationServiceImpl implements AuthenticationService { private String pwd = "somePwd"; // assume it's got at runtime. @Override public boolean authenticate(String inputPwd) { String decryptedInputPwd = decrypt(inputPwd); return pwd.equals(decryptedInputPwd); } private static String decrypt(String inputPwd) { return inputPwd; // assume it's decrypted with private key } }
Host 模塊使用機密計算服務(wù)的代碼示例如代碼塊 3 所示,從中可以看到對機密計算服務(wù) AuthenticationService 接口的使用和普通的 SPI 接口別無二致,依然是加載服務(wù)、調(diào)用函數(shù)、根據(jù)結(jié)果執(zhí)行不同的動作等過程。稍有區(qū)別的地方在于先要創(chuàng)建出機密計算環(huán)境 Enclave 的實例,然后從中加載機密計算服務(wù)實例,由此將機密計算的服務(wù)實例和環(huán)境實例綁定,最后再銷毀環(huán)境。這些機密計算環(huán)境生命周期管理的 API 由 Teaclave Java 提供。從代碼塊 3 中可見,在 Host 模塊中無需感知密碼和私鑰究竟是什么,也不用了解認證的過程,只是將認證函數(shù)當(dāng)作黑盒服務(wù)調(diào)用。
代碼塊 3 從 Host 模塊使用機密計算服務(wù)示例
public class Main { public static void main(String[] args) throws Exception { Enclave enclave = EnclaveFactory.create(); Iteratorservices = enclave.load(AuthenticationService.class); String pwd = "encryptedPwd"; // assume this is an encrypted password while (services.hasNext()) { AuthenticationService authenticationService = services.next(); if (authenticationService.authenticate(pwd)) { System.out.println("Passed"); } else { System.out.println("Rejected"); } } enclave.destroy(); } }
以上三部分代碼就構(gòu)成了一個完整的 Java 機密計算應(yīng)用。從開發(fā)的角度看起來與編寫一個普通的 SPI 服務(wù)調(diào)用的應(yīng)用基本一樣,只需要專注于業(yè)務(wù)邏輯的開發(fā)即可,并不需要學(xué)習(xí)機密計算底層的內(nèi)容。因此 Teaclave Java 將 Java 機密計算的開發(fā)門檻降低到了 0。
構(gòu)建機密計算應(yīng)用
Teaclave Java 提供了一套完整的構(gòu)建工具鏈以支持上文所述的編程模型,用戶只需輸入幾個簡單的 maven 命令即可完成全部構(gòu)建任務(wù)。構(gòu)建工具鏈將非機密代碼和機密代碼分別編譯為 Java bytecode 產(chǎn)物和可部署于 SGX 中的 native 庫,以及自動生成完成機密計算服務(wù)調(diào)用所需的所有的輔助代碼。
圖 4 展示了 Teaclave Java 的構(gòu)建部署視圖,主要包括三方面內(nèi)容:
1)Host 和 Common 模塊被編譯為普通的 Java bytecode,部署在普通環(huán)境中執(zhí)行。
2)Enclave 和它所使用到的 Common 模塊中的內(nèi)容被編譯為 native 機密庫文件,部署在 SGX 硬件中執(zhí)行。
3)從 Java bytecode 到 native code 之間并不能直接調(diào)用,而需要一些適配轉(zhuǎn)換工作,包括:
服務(wù)代理:通過 J ava 的動態(tài)代理機制將 Host 模塊中的機密計算服務(wù)調(diào)用代理到實際的 native 函數(shù)上,并完成上下文環(huán)境的同步、服務(wù)參數(shù)和返回值的序列化反序列化等工作。
JNI 層:Java 側(cè)的 native 函數(shù)聲明、native 側(cè)的 JNI 函數(shù)聲明和到機密庫函數(shù)的調(diào)用等輔助代碼。
(圖 4 Teaclave Java 部署視圖)
這些適配轉(zhuǎn)換調(diào)用的代碼在構(gòu)建中被自動生成,分別部署在普通環(huán)境和 SGX 中,在圖 4 中它們被用藍色標出。
構(gòu)建過程中的重要一步是將機密部分的 Java 代碼編譯為 native 代碼的 Java 靜態(tài)編譯。
Java 靜態(tài)編譯
Java 程序原本需要在 JVM 上才能運行,但是 Java 靜態(tài)編譯技術(shù)可以將 Java 程序(包括 JDK 庫依賴和三方庫依賴)加上必要的運行時支持代碼一起編譯為 native 代碼,然后直接運行。以此實現(xiàn)了 Java 程序無需 JVM 的輕量級運行。
Teaclave Java 采用了目前最成熟的 Java 靜態(tài)編譯技術(shù)——Oracle 主導(dǎo)的開源項目 GraalVM 進行 Java 靜態(tài)編譯。GraalVM 首先對 Java 程序做可達性分析,找到從程序入口開始的所有可能執(zhí)行到的代碼范圍,然后僅編譯這些可達的代碼,得到一個 native 制品(被稱為 native image)。程序入口對于可執(zhí)行程序來說是 main 函數(shù),對于庫文件來說是暴露的公共 API。具體到 Teaclave Java 場景,入口就是開發(fā)者定義的機密計算服務(wù)函數(shù),也就是 Enclave 模塊中定義的機密計算服務(wù)接口的實現(xiàn)函數(shù)。這些接口實現(xiàn)會用到三種依賴,Common 模塊中的代碼、某些 JDK 庫以及其他 Java 三方庫,但是只會用到這些依賴的部分代碼,而非全部代碼。
GraalVM 就會將實際用到的代碼分析出來,與機密計算服務(wù)的實現(xiàn)代碼和 GraalVM 提供的運行時支持(被稱為 Substrate VM)一起編譯為 native image。但是 GraalVM 是面向通用場景和硬件平臺的,所以 Teaclave Java 為其額外提供了針對 SGX 硬件平臺的適配和機密計算需求的優(yōu)化。當(dāng)我們編譯出 native image 后,會發(fā)現(xiàn)其具有了一些特別的性質(zhì):
TCB 下降。GraalVM 僅編譯從機密計算服務(wù)入口可達的代碼,因此與 Occlum 將 LibOS、JVM 和 Java 應(yīng)用全部放入 TEE 的方案相比,TCB 大幅降低了。
安全性提升。Native image 在運行時有自己的 native 內(nèi)存堆,它與 Java 堆是相互隔離的,從 Java 應(yīng)用中很難被訪問到(Java 通過 Unsafe 接口依然可以訪問 native 內(nèi)存,但是難度提升很多)。而且 Java 靜態(tài)編譯去掉了 Java 的動態(tài)特性,只有在編譯時經(jīng)過顯式配置的反射和動態(tài)類加載才會生效,其他運行時的動態(tài)行為是無效的。Log4j 漏洞攻擊在 native image 上本身就是無效的。因此 native image 可以被視作一個安全沙箱,即使沒有 SGX 硬件環(huán)境,native image 相比 Java 程序也提升了安全性。部署在 SGX 里之后,TEE 的安全性會更高,因為消除了 Java 動態(tài)特性對 TEE 安全性的威脅。
性能提升。GraalVM 的 Java 靜態(tài)編譯對代碼有相當(dāng)程度的編譯優(yōu)化,其運行時性能大致可以達到 JVM 的 C1 優(yōu)化水平,再加上無需啟動 JVM、沒有類加載過程、沒有解釋執(zhí)行、沒有 JIT 消耗資源等等,在執(zhí)行短小的任務(wù)時與 Java 程序相比能有 1 個數(shù)量級的性能提升,內(nèi)存也有大幅削減。
這些性質(zhì)可以有效地提升機密程序的安全性,提升了 Teaclave Java 的實用性。
Teaclave Java 技術(shù)評估
以上介紹了 Teaclave Java 提供的 Java 機密計算編程模型和采用的構(gòu)建方式等技術(shù)問題,那么最終實現(xiàn)的效果如何呢?本文以 log4j 漏洞攻擊為例分析 Teaclave Java 的功能有效性。
在 TCB 改進和運行時性能分析方面,我們準備了如表格 1 所示的 10 個測試。前 4 個“app-”前綴的是我們自己寫的簡單應(yīng)用,將它們當(dāng)作機密程序,以各自的 main 入口當(dāng)作普通程序。后 6 個“ct-”前綴的用例則采用了著名開源加密框架 BouncyCastle(https://www.bouncycastle.org/java.html)的單元測試,我們提供了單一入口調(diào)用這些測試,將測試入口當(dāng)作普通程序,單元測試當(dāng)作機密程序。
(表 1 / 測試用例描述)
Java 機密計算框架則采用了 OcclumJ 和 Teaclave Java 進行對比。OcclumJ 是我們實現(xiàn)的一種介于 Occlum 和 Teaclave Java 之間的機密計算模型,采用 Teaclave Java 的模塊化和機密計算服務(wù)化的模型,但是不做 Java 靜態(tài)編譯,而是在 TEE 中以 Occlum 方式運行機密計算服務(wù)。
功能有效性評估
圖 5 給出了 log4j 漏洞攻擊的原理示意(a 子圖)和 Teaclave Java 防范 log4j 漏洞攻擊的原理(b 子圖)。對于一個普通的 Java 應(yīng)用服務(wù),它和客戶端通過三個步驟交互。
1)客戶端從服務(wù)端獲取公鑰。
2)客戶端用公鑰對消息加密,然后將密文發(fā)送給服務(wù)端。
3)服務(wù)端從運行時內(nèi)存中拿出私鑰解密消息,然后再處理消息內(nèi)容。假設(shè)服務(wù)端使用了 log4j-2.14.x 版本做日志記錄,其中的漏洞允許攻擊者誘導(dǎo) log4j 從遠程服務(wù)器下載指定的惡意 class 文件(圖 5-a 中的步驟 4、5、6),然后動態(tài)加載惡意類,從 Java 堆內(nèi)存上獲取到私鑰(步驟 7)傳給攻擊者。
有了服務(wù)器的私鑰,服務(wù)器和客戶端之間的所有通信對于攻擊者而言都如同明文了。
a. log4j 漏洞攻擊示意
b. 通過機密計算免疫 log4j 漏洞攻擊示意
(圖 5 / Java 機密計算保護應(yīng)用免 受 Log4j 漏洞攻擊示意圖)
圖 5-b 展示了 Teaclave Java 如何保護應(yīng)用服務(wù)端免受 log4j 漏洞攻擊的威脅。Teaclave Java 將應(yīng)用的普通代碼放在 REE 中,安全敏感的解密和私鑰放在 TEE 中,客戶端送來加密消息會被 REE 中的代理服務(wù)轉(zhuǎn)到 TEE 中進行解密。此時當(dāng)攻擊者發(fā)起 log4j 攻擊時,因為 Log4j 部署在 REE 中,惡意代碼也只能在 REE 中運行,而無法拿到 TEE 內(nèi)存上的私鑰,攻擊失效。假如機密代碼也使用了 log4j 記日志,導(dǎo)致 log4j 運行在 TEE 中運行會發(fā)生什么呢?
此時 log4j 將攻擊者惡意代碼下載到了 TEE 中,但是因為 Teaclave Java 采用了 Java 靜態(tài)編譯技術(shù),惡意代碼在編譯時是未知的,不會被編譯到 native image 中。而 Java 靜態(tài)編譯技術(shù)并不支持對 native image 中不存在的代碼進行動態(tài)加載執(zhí)行,所以即便惡意類被下載到了 TEE,也不會被執(zhí)行。因此在這種場景下 Teaclave Java 支持的機密計算依然是安全的。但是如果采用了 Occlum 方案,因為 TEE 中有了 JVM,就可以動態(tài)加載惡意代碼并運行,攻擊就會成功。
再退一步,當(dāng)在沒有 SGX 硬件為 TEE 加密時,native image 依然是一個 native 沙箱,惡意 Java 代碼無法輕易從 native 內(nèi)存中拿到安全敏感內(nèi)容。
TCB 評估
因為 Teaclave Java 不再需要 LibOS 和 JVM,機密代碼部分也是按需編譯部署。OcclumJ 方案雖然采用了分模塊模型,但是并沒有做靜態(tài)分析,因此只是模塊級別的代碼劃分,雖然較 Occlum 完全不劃分有所改進,但是與 Teaclave Java 函數(shù)級的劃分相比仍有相當(dāng)大的差距。圖 6 展示了 OcclumJ 和 Teaclave Java 放入 TEE 的二進制編譯產(chǎn)物的大小對比。藍條是 OcclumJ 的結(jié)果,橙條是 Teaclave Java 的結(jié)果,圖中的 Lejacon 是 Teaclave Java 在論文中的代號。
(圖 6 Occlum 和 T eaclave Java 方案的 TCB 比較。Lejacon 是 Teaclave Java 在論文中的代號)
由圖 6 中的對比數(shù)據(jù)可見,Teaclave Java 的編譯后 TCB 大小僅為 Occlum 的大約 1/20 到 1/10??紤]到編譯時 native 代碼的膨脹問題,兩者實際的函數(shù)數(shù)量差距更大,所以 Teaclave Java 的 TCB 低于 Occlum 一個數(shù)量級,從而具有了更高的安全性。
運行時性能評估
因為 native image 會直接以 native 代碼的形式運行,省去了包括 JVM 啟動、類加載、解釋執(zhí)行等步驟的 Java 程序的冷啟動過程,所以啟動速度會非常快。如果要執(zhí)行的機密代碼較少,會很快執(zhí)行完畢。但是 native image 的代碼編譯質(zhì)量不如 JVM 的 C2,所以當(dāng)程序執(zhí)行的時間足夠長,Java 代碼被 JIT 充分編譯后,native image 的運行時性能就會隨著時間的增長而與 Java 程序越來越接近,然后被超越。所以 Teaclave Java 在小型應(yīng)用的性能遠優(yōu)于 OcclumJ,但是在長時間執(zhí)行的應(yīng)用方面該優(yōu)勢就會縮小。
圖 7 就展現(xiàn)出了這一特點。圖中的藍線是機密代碼部分采用 OcclumJ 模型的執(zhí)行時間,黃線是采用 Teaclave Java 模型的執(zhí)行時間,綠線是在普通環(huán)境中在普通 JVM 上直接運行的時間。程序在 TEE 中運行的時間要大于普通環(huán)境,主要因為增加了機密環(huán)境的創(chuàng)建、機密內(nèi)存的分配等開銷,我們將其統(tǒng)稱為機密環(huán)境開銷。黃線在執(zhí)行時間較短的場景中保持了與綠線接近的性能,說明 Java 程序冷啟動的開銷與 native ?image 的機密環(huán)境開銷差不多可以相抵。當(dāng)程序執(zhí)行時間較長時,冷啟動開銷被攤薄,但是機密環(huán)境開銷與 TEE 內(nèi)存使用量成正比,所以黃線較綠線在最后三個測試用例上的上升線條更陡峭。
圖 8 給出了 OcclumJ 和 Teaclave Java 的運行時內(nèi)存使用量對比。OcclumJ 的內(nèi)存消耗包括 LibOS、JVM 和應(yīng)用三部分,而 Teaclave Java 模型的內(nèi)存消耗只有應(yīng)用和 native image 中的輕量級運行時。更簡化的結(jié)構(gòu)為 Teaclave Java 模式的機密計算帶來了更少的內(nèi)存消耗。
(圖 7 OcclumJ 和 Teaclave Java 運行時性能對比圖。Lejacon 是 Teaclave Java 在論文中的代號)
(圖 8 / OcclumJ 與 Teaclave Java 的運行時內(nèi)存消耗對比圖。Lejacon 是 Teaclave Java 在論文中的代號)
總? 結(jié)
Teaclave Java 是一個使用簡單、效果顯著、性能良好的 Java 機密計算解決方案,能夠幫助用戶徹底解決保護 Java 應(yīng)用中的安全敏感內(nèi)容和運算的問題。Teaclave Java 具有硬件寬容性,當(dāng)具備 SGX 硬件環(huán)境時,可以使 Java 用戶也能像其他 native 語言用戶一樣享受到機密計算帶來的 最高等級運行時安全保護;在缺少機密計算的硬件環(huán)境時,仍然可以提供 安全沙箱 對機密代碼實施內(nèi)存隔離,以避免安全敏感內(nèi)容直接暴露。可以說,Teaclave Java 就是保護 Java 應(yīng)用中敏感數(shù)據(jù)和運算安全的標準答案。
Oracle 已經(jīng)把 GraalVM 的 Java 靜態(tài)編譯技術(shù)貢獻給了 OpenJDK,預(yù)計在 JDK 21 會合入 OpenJDK 主干。因此在未來 Teaclave Java 方案就可以獲得 JDK 的原生支持。我們也計劃向 Java 社區(qū)提交關(guān)于增加機密計算規(guī)范的文件,希望可以將 Teaclave Java 的機密計算模型上升為 Java 原生的機密計算方案。
編輯:黃飛
?
評論