Java語言擁有三大特征:平台無關性、網絡移動性和安全性,而Java體系結構對這三大特征提供了強大的支持和保證,本文著重介紹Java體系結構對支持信息安全的原理和使用方法。
Java體系結構
Java的體系結構如下圖所示,首先Java的源代碼Java文件由編譯器編譯成Java的二進制字節碼class文件,然後class文件由Java虛擬機中的類裝載器進行加載,同時類裝載器還會加載Java的原始 API Class文件,類加載器主要負責加載、連接和初始化這些class文件以後,就交給虛擬機中的執行引擎運行,執行引擎將class文件中的Java指令解釋成具體的本地操作系統方法來執行,而安全管理器將在執行過程中根據設置的安全策略控制指令對外部資源的訪問。
Java的執行方式不是編譯執行而是解釋執行,不同平台上面相同的源代碼編譯成符合Java規范的相同的二進制字節碼,然後再交給支持各自平台的虛擬機去解釋執行,"先編譯,後解釋,再執行"三步走的方式使得Java實現了"一次編寫,到處運行",如果Java應用使用的是100%標准Java API並且沒有直接調用本地方法,那就可以不加修改地運用在多種平台上,這樣的平台無關性使得在異構的網絡環境或者嵌入式方面的應用更方便和現實。Java的網絡移動性帶來了一種全新的軟件模式,在分布式處理模式的基礎之上,可以將軟件和數據通過網絡傳送到客戶端去,這樣確保了客戶端有必備的軟件來浏覽和操縱通過網絡傳輸的數據,Java體系結構支持把單一的執行文件切割成小的二進制字節碼文件Class文件,而這些文件可以按照應用的需要動態連接、動態擴展。Java體系結構對安全性的支持主要是通過Java語言本身安全性、虛擬機的類加載器和安全管理器以及Java提供的安全API幾個方面來實現:防止惡意程序的攻擊,程序不能破壞用戶計算機環境;防止入侵,程序不能獲取主機或所在內網的保密信息;鑒別,驗證程序提供者和使用者的身份;加密,對傳輸交換的數據進行加密,或者給持久化的數據進行加密;驗證,對操作設置規則並且進行驗證。
Java信息安全的必要性
隨著互聯網應用越來越廣泛,並且互聯網其本身獨特的資源共享性,因此能夠按照用戶需求及時准確獲得信息和處理信息的應用對用戶而言就相當重要,這也是Java得以迅速發展和被廣泛接受的原因。但同時網絡也提供了一條攻擊接入計算機的潛在途徑,特別是當用戶下載網絡軟件在本地運行,這就要求Java能夠對病毒/木馬的問題加以防范,對信息以及本地環境進行保護。比如我們浏覽一個網頁的時候,網頁上的Applet可能會自動下載並且運行,而這個Applet完全有可能來自不可靠的地方,又或者我們使用通過JINI服務查找到的網絡上不可靠的服務對象來獲得服務,如果沒有Java體系結構提供的安全機制,這就很有可能引入了一個懷有敵意的程序造成信息丟失、資料洩密、相信偽造數據和修改本地計算機安全設置等等後果,帶來未知的嚴重後果。
Java語言本身安全性
Java語言的設計者們是在C++的基礎上設計出來Java的,因此與C++相比它的語法更加簡單清晰,結構、單元、運算符重載、虛擬基礎類等在Java中都沒有采用,並且取消了多重繼承而采用實現多個接口的方式。這樣能降低開發人員犯錯誤的幾率,幫助他們寫出更安全的代碼。
Java中去除了C++語言中的令人費解、容易出錯的"指針",用列表、堆、哈希表等結構來代替,避免了任何不安全的結構。Java也沒有索引核查的數組訪問,因為這往往會導致不定的、不可預測的程序操作,它所有的數組訪問都必須先檢查是否越界。Java要求所有的變量在初始化以前不能使用,對於基本數據類型變量都會自動地賦給某個初始值,避免了未初始化變量獲取內存信息。所有這些都使得程序不能訪問任意的內存地址,對於內存中的實體信息只能通過有權限的對象進行訪問,而不會出現象C++那樣把類型指針強制轉換成內存的指針,然後通過內存查找的方法找到私有的變量。
Java分配內存對於開發人員來說是透明的,開發人員使用new方法新建對象,這時候虛擬機就會從堆內存中找到合適的內存空間,開發人員不需要也不能夠進行干預。而對於內存的回收,Java避免了開發人員明確干預對象的回收,比如C的free或C++的delete命令,避免了開發人員無意間對內存的破壞。Java采用虛擬機的"垃圾回收"機制來實現的內存自動管理,釋放不再被使用的內存資源,內存回收器就像一台垃圾收集車,但是和我們在大街上看到的收集車,僅僅收集大家放在垃圾桶裡面的垃圾不同的是,它還要到你家裡去幫你找出那些東西是不要用的垃圾,然後把這些東西拿走,最後還要整理家裡的空間,騰出最大的空間讓你放新東西。Java的內存回收器目的就是找到不再引用的對象,釋放內存空間,並且需要整理內存的碎片空間,盡量避免出現"內存不足"的情況。
對於在網絡中交換的序列化對象很容易在重建對象的時候訪問到對象的私有信息,這時候Java提供了兩種辦法來保護信息,一種就是采用給變量加上transient關鍵字的方法,這樣對象序列化的時候就不會讀寫該變量,另一種就是在實現Externalizable接口而不是Serizlizable接口,這樣對象就只能通過writeExternal和readExternal方法來保存和重建,其他方法無法進行了。
以上這些都是Java語言本身對信息安全提供的基礎。
類加載器
雖然名字叫類加載器,但是實際上Java虛擬機中的類加載器不光要負責加載而且要負責連接和初始化應用程序需要用到的Java類型。加載就是把二進制形式的字節碼讀入虛擬機中,而連接就是給這個已經讀入的類型分配類變量內存以及把類型中用到常量池中的符號轉換為直接引用,最後的初始化過程就是賦給類型變量合適的初始值。
類加載器為加載的類提供了不同的命名空間,統一源代碼生成的字節碼被加載到同一個命名空間中,相同命名空間不能加載類名相同的類,同一個命名空間內的類可以直接進行交互,而不同的命名空間的類是不能交互的,除非顯式地提供了交互機制,通過命名空間和類成員訪問權限的設置保護了被信任的類邊界。
類加載器分成了啟動類加載器、標准擴展類加載器、路徑類加載器和網絡類加載器四種。啟動類加載器從本地系統中加載原始的Java API類,用來啟動Java虛擬機,而其他三種加載器是在運行時加載用戶定義的類,標准擴展類加載器加載的是不同虛擬機提供商擴展的標准Java類,而在classpath中的類由路徑類加載器來加載,網絡類加載器加載通過網絡下載得到的類文件,每一種加載器在加載類的時候都會建立一個加載器實例。類加載器采用雙親委派鏈模式(這個模式很類似GOF在《設計模式》一書中提到的責任鏈模式)除了啟動類加載器以外,每個類加載器都有自己的"雙親"。一個類可以通過有三種方法定義自己的雙親:第一種通過引用,比如A類中引用了B類(即A和B有關聯關系),那麼B類的加載器就會作為A類的加載器的"雙親",早於A類加載;第二種使用loadClass方法來自定義"雙親",這時被load的類的"雙親"即本身這個類加載器;第三種在沒有采用前兩種的情況下使用的默認方式,默認把啟動類加載器作為"雙親"。