如何防范Java反序列化漏洞
Java 編程語言提供了一種無縫且優雅的方式來存儲和檢索數據。但是,如果沒有適當的輸入驗證和保護措施,您的應用程序可能容易受到不安全的反序列化漏洞的攻擊。
在最好的情況下,反序列化漏洞可能只會導致數據損壞或應用程序崩潰,從而導致拒絕服務 (DoS) 條件。很多時候,遠程攻擊者可以觸發相同的漏洞,從而在易受攻擊的系統上實現任意代碼執行能力。
Java中的序列化是什么?
序列化是指將對象狀態保存為字節序列的過程,相反,反序列化是將這些字節重新構建回對象的過程。
假設您剛剛開發了一個在本地讀取和寫入數據的應用程序,例如從系統上存在的文件中讀取和寫入數據。或者您構建了一個通過網絡發送和接收數據的應用程序。在保持數據完整性的同時做到這一點的最佳方法是什么?
就存儲而言,選擇將數據存儲在文件中還是數據庫中仍然取決于開發人員。即便如此,當涉及到通過網絡傳輸數據時,您必須選擇合適的數據格式和編碼機制來“標準化”數據,并且最好是獨立于平臺的。
存在許多解決方案,包括手動將二進制或文本數據轉換為其簡單的base64 ASCII 格式并對其進行解碼。但是為什么要重新發明輪子來實現數據編碼和解碼機制呢?Java 的內置序列化概念為您完成了所有這些,對于您的應用程序創建的仍在內存中的對象。
Java Serialization API 為開發人員提供了處理對象序列化的標準機制。
例如,假設您在 Java 中有一個“Person”類,其中包含包含個人個人信息的字段,例如“姓名”、“電子郵件地址”、“電話號碼”和“地址”。如果您想為用戶提供“保存”選項,您可以選擇遍歷“Person”對象,將每個字段轉換為適當的格式,例如 JSON 或 CSV,然后將其輸出到文件中。
如果您不關心結果文件的可讀性,而只想存儲這些數據以供您的應用程序以后檢索,序列化可以為您節省大量時間。通過序列化,您可以使用單個命令簡單地將“Person”對象或多個“Person”對象的數組(列表)轉儲到一個文件中。數據的編碼由 Java 的內置序列化庫負責。
使序列化成為對開發人員有吸引力的解決方案的原因在于,只需一個命令即可實現數據的存儲、檢索和傳輸,而無需擔心底層邏輯或平臺。自然地,許多應用程序和開發人員依賴序列化來存儲數據和對象的狀態。
支持序列化的“Person”類的一個簡單示例是:

不安全的對象反序列化漏洞是如何發生的
假設您的 Java 應用程序從文件或網絡流中反序列化數據,并從中檢索先前序列化的“Person”對象。您的應用程序是否應該期待一個“Person”對象,但接收到一個“Animal”對象——無論是錯誤的還是故意由于惡意活動,會發生什么?
在大多數情況下,可能會出現一條錯誤消息,導致應用程序崩潰,最終導致由損壞的數據觸發的 DoS 條件。在更高級的情況下,根據對象的使用方式,密切相關的類可能能夠觸發遠程代碼執行 (RCE)。例如,當應用程序期望接收包含序列化 Java 對象的“配置”數據或有效負載時,就會發生這種情況。
例如,今年 7 月,ForgeRock 的 OpenAM 中的一個嚴重漏洞(CVE-2021-35464)源于應用程序使用的 Jato 框架中不安全的 Java 反序列化。通過簡單的 GET 請求,攻擊者可以將精心設計的序列化對象發送到服務器并執行其惡意代碼。PortSwigger 研究員 Michael Stepankin 演示的 PoC 漏洞詳細解釋了這一點。
http://server.example.com/openam/oauth2/..;/ccversion/Version?jato.pageSession=<serialized_object>
最近,Atlassian 開始向企業客戶發送電子郵件,以修補關鍵的 JIRA 數據中心漏洞CVE-2020-36239,該漏洞可能讓遠程攻擊者在易受攻擊的服務器上執行任意代碼。漏洞的原因?不安全的反序列化和暴露的端口。攻擊者可以將精心設計的有效載荷發送到暴露的 Ehcache RMI 網絡服務端口 40001 和潛在的 40011 并實現代碼執行。
反序列化漏洞不僅影響 Java 應用程序
Java 不是唯一受不安全反序列化漏洞影響的編程語言。Microsoft .NET 語言也支持序列化,這意味著反序列化數據的安全不足的 .NET 應用程序可能會帶來風險。
不久前,一個名為 Praying Mantis (TG1021) 的威脅行為者組織的目標是運行易受攻擊的 ASP.NET 應用程序的 IIS 服務器。ASP.NET 應用程序“Checkbox”中的零日漏洞讓遠程攻擊者可以執行源自不安全反序列化的任意代碼。
由于如此多的 Java 和 .NET 應用程序依賴于序列化來存儲和交換信息,當應用程序缺乏基本的輸入清理或托管在不夠安全的服務器上(例如暴露的端口或未正確驗證的 API 端點)時,威脅參與者可以利用更大的風險面。
如何防止不安全的反序列化?
一個明顯的方法是在從反序列化的字節流中解析對象時執行基本的輸入清理。防止不安全反序列化攻擊的另一個基本要素是僅允許反序列化某些類型(類)的對象。這消除了您的應用程序面臨的任何歧義,并且是一種避免應用程序崩潰或 DoS 攻擊可能性的優雅方式。
有兩種方法可以做到這一點:遵循黑名單方法——即明確禁止某些類的對象被反序列化——或者更嚴格的白名單方法。盡管有限制,但白名單方法往往更安全,因為只有屬于預先批準的一組類的對象才會被應用程序反序列化,從而防止出現任何意外。
流行的 Java 項目 Jackson Databind之前已經針對反序列化缺陷實施了兩種類型的修復。在最長的時間內,該項目采用了更允許的黑名單方法,并且會不時地將禁用的小工具/類添加到同一列表中:

但是,較新的修復程序通過引入“ PolymorphicTypeValidator ”類來遵循更具選擇性的白名單方法。只有屬于列表的類的對象才會被反序列化。以下是如何在實踐中完成此類的示例:

顯示的示例代碼只允許反序列化“com.gypsyengineer.jackson”類型的對象。
無論您選擇使用哪種方法,這里的基本原則仍然是永遠不要相信輸入,即使輸入似乎來自權威來源或應用程序(而不是用戶)。在處理輸入之前執行基本的清理檢查可以幫助防止重大漏洞利用。
對于感興趣的研究人員和滲透測試人員,一個名為ysoserial的 GitHub 存儲庫包含一組實用程序和面向屬性的編程小工具鏈,通常可以在常見的 Java 庫中找到。在適當的條件下,這些小工具鏈可以幫助進行不安全的反序列化攻擊——這是一種合理的方法來檢查您的 Java 應用程序是否可以通過不安全的反序列化被高級威脅參與者利用。