天天看點

《Java學習指南》—— 1.5 實作安全

本節書摘來異步社群《java學習指南》一書中的第1章,第1.5節,作者:【美】patrick niemeyer , daniel leuck,更多章節内容可以通路雲栖社群“異步社群”公衆号檢視。

建立一種語言進而使自己免于自我傷害,這是一回事;而建立一種語言避免别人攻擊你則是另一回事。

封裝(encapsulation)是一種将資料和操作隐藏于類中的概念,它是面向對象設計中的重要部分。它将有助于編寫簡潔的子產品化軟體。不過,在大多數語言中,資料項的可見性隻展現為程式員和編譯器之間關系的一部分,這是一個語義問題,而并非資料在運作程式環境中實際安全性的斷言。

bjarne stroustrup在c++ 中選擇了關鍵字private來指定類的隐藏成員,他可能考慮到了令你免于陷入某個類開發人員所寫代碼的繁雜細節中,但是卻沒有考慮到另一個問題,即保護該開發人員的類和對象免于遭受其他人的病毒和特洛伊木馬等的攻擊。在c或c++ 中可以進行任意的類型強制轉換和指針算術運算,這就使得很容易違反類的通路許可,而并不違反該語言的規則。請考慮以下代碼:

《Java學習指南》—— 1.5 實作安全

在這段c++的代碼,我們編寫了一些違反finances類封裝性的代碼,并竊取了一些秘密資訊。這種詭計(即濫用無類型指針)在java中是不能得逞的。如果這個例子還顯得有些不切實際,請設想一下,保護運作時環境的基類(系統類)免受類似攻擊該是何等重要。如果不可信代碼可以破壞對實際資源(如檔案系統、網絡或視窗系統等)提供通路的元件,那麼它自然有機會竊得你的信用卡号。

如果一個java應用要從internet上的某個不可信來源動态地下載下傳代碼,并且将它與可能包含機密資訊的其他應用一同運作,那麼就必須盡可能加大保護的力度。java安全模型在所導入類周圍包裝了3個保護層,如圖1-3所示。

《Java學習指南》—— 1.5 實作安全

在外層,應用級安全決策由一個安全管理器做出。安全管理器控制對檔案系統、網絡端口和視窗環境的這樣的系統資源的通路。安全管理器要依賴于類加載器的功能來保護基本的系統類。類加載器負責處理從本地存儲或網絡加載的。在内層,所有系統安全性最終都要取決于java校驗器,它将確定導入的類的完整性。

java位元組碼校驗器是java運作時系統中的一個固定部分。而類加載器和安全管理器(或者更确切地說,應稱為安全政策)則是可以有不同實作的元件,即對于加載位元組碼的不同應用(如applet浏覽器和web浏覽器)來說,可以用不同的方式來實作類加載器和安全管理器。以上三者都必須正常工作才能確定java環境的安全性。

java的第一道防線是位元組碼校驗器。校驗器在位元組碼運作前先讀取它,并確定其行為正常而且遵循java語言的基本原則。一個可信的java編譯器是不會生成反常的位元組碼的。不過,有惡意的人也有可能故意組合出不好的代碼,對此加以檢測正是校驗器的任務。

一旦代碼得到校驗,就可以認為是安全的,即不存在某些偶然或惡意的錯誤。例如,得到校驗的代碼不會仿造引用或違反對象的通路許可(就像前面舉的信用卡的例子那樣)。它也不會完成非法的類型強制轉換或者以非法的方式使用對象。此代碼甚至不會導緻某些内部錯誤,如操作數棧上溢出或下溢出。這些就構成了java安全性的堅實基礎。

你可能會有所疑問,許多解釋語言不是也隐含地有這種安全性嗎?不錯,确實如此,例如你無法借助惡意的basic代碼來破壞解釋器,但是要記住,大多數解釋語言中的保護都發生在一個更高層次上。這些語言往往有重量級的解釋器,它們要完成大量運作時工作,是以無可避免地會較慢,也較為笨重。

比較而言,java位元組碼則是相對輕巧的低級指令集。在java位元組碼執行前能夠對其靜态地加以校驗,這就使得java解釋器可以全速地運作,并且有充分的安全性,此外還不必進行代價昂貴的運作時檢查。這是java重要的創新之一。

校驗器是一種數學“定理證明機”。它從java位元組碼入手,應用簡單的推導規則來确定位元組碼在某些方面将是如何表現的。較之于此類其他語言的代碼,已編譯java位元組碼中包含有更多的類型資訊,是以這種分析是可能的。位元組碼還必須遵循一些額外的規則以簡化其操作。首先,大多數位元組碼指令都隻是在單個的資料類型上操作。例如,對于棧操作,在java中有關對象引用的指令和有關各種數字類型的指令都是不同的。類似地,将各種類型的值移入或移出某個局部變量時,也有不同的指令。

其次,對于由任何操作得到的對象,其類型總是提前預知的。不存在使用值的位元組碼操作,也不存在生成多種類型的值作為輸出的位元組碼操作。是以,總是可以通過檢視下一條指令及其操作數來了解将得到的值的類型。

由于操作總是産生一個已知的類型,是以通過檢視起始狀态,則有可能确定将來某一時刻棧中以及局部變量中所有元素的類型。某一時刻所有類型資訊的集合稱為棧的類型狀态(type state),java在運作應用之前試圖對此加以分析。java對于此時棧和變量元素的實際值一無所知,隻是知道它們是何種元素。不過,這些資訊對于加強安全規則以及確定對象不會被非法操作已經足夠了。

為了使得對棧的類型狀态進行分析變得真正可行,對于java位元組碼指令如何執行,java設定了一個額外限制,即到達代碼中同一點的所有路徑都必須有相同的類型狀态。

java還增加了第2個安全層,即類加載器。類加載器負責将一個或多個java類的位元組碼置入到解釋器中。每一個要從網絡加載類的應用都必須使用類加載器來處理這一任務。

類已得到加載并且通過了校驗器檢查之後,它将保持與其類加載器的關聯。由此,類可以根據其來源而有效地劃分到不同的命名空間中。當一個已加載類引用另一個類名時,新類的位置則由原來的類加載器提供。這說明,從一個指定來源擷取得到的類,隻能與從同一位置得到的其他類進行互動。例如,支援java的web浏覽器可以使用類加載器為從一個給定url加載的所有類建構一個單獨的空間。基于密碼簽名的類這樣的進階安全性,也可以使用類加載器來實作。

對類的搜尋總是從内建java系統類開始。這些類從java解釋器的類路徑(classpath,見第3章)所指定的位置加載。類路徑中的類僅由系統加載一次,而且不能被替換。這說明,應用程式不可能使用自己的版本來替換基本系統類以期修改其功能。

最後,安全管理器要負責做出應用級安全決策。安全管理器是一個可以由應用安裝的對象,用以限制對系統資源的通路。應用每次試圖通路諸如檔案系統、網絡端口、外部程序以及視窗環境等内容時,都會詢問安全管理器;安全管理器可以允許也可以拒絕相應的請求。

對于将不可信代碼作為其正常操作一部分來運作的應用來說,安全管理器至關重要。例如,一個支援java的web浏覽器可以運作applet,而applet可能來自于網絡上的不可信的源,這樣的浏覽器首當其沖就需要安裝一個安全管理器。此安全管理器再對此後所允許的通路類型加以限制。這就使得應用在運作任意一段代碼前都可以施加一個有效的可信級别。而且一旦安裝了安全管理器,它就不能被替換。

安全管理器與一個通路控制器協同工作,通過編輯一個聲明性的安全政策檔案,進而允許我們從一個較高的層級來實作安全政策。通路政策可能很簡單,也可能相當複雜,這取決于特定的應用。有時隻需拒絕對所有資源的通路,或者拒絕對于諸如檔案系統或網絡等通用服務的通路,盡管這種政策很簡單,但也已經足夠。不過,基于進階資訊來制定複雜的決策也是可能的。例如,一個支援java的web浏覽器可以使用一個通路政策,進而使使用者能夠指定applet的信任程度,或者允許或拒絕對特定資源的通路,在此可以針對每種不同情況有不同的設定。當然,這裡假設浏覽器能夠确定必須相信哪些applet。稍後将介紹這一問題是如何通過代碼标記解決的。

安全管理器的完整性依賴于java安全模型在更低層次所提供的保護。如果沒有校驗器和類加載器所提供的保證,有關系統資源安全性的進階斷言就沒有意義。由java位元組碼校驗器所提供的安全表明,解釋器不能被破壞或攪擾,而且java代碼必須如期使用元件。這反過來說明了類加載器可以保證應用在使用核心java系統類,而且這些類是通路基本系統資源的唯一途徑。适當地施以如上的限制,就可以用一個安全管理器以及使用者定義的政策來集中把握對這些資源的控制了。

本文僅用于學習和交流目的,不代表異步社群觀點。非商業轉載請注明作譯者、出處,并保留本文的原始連結。