因為網絡允許多台計算機共享數據和分布式處理,所以它提供了一條入侵計算機系統的潛在途徑,使得其他人可以竊取信息,改變或破壞信息,盜取計算機資源等等。為了解決由網絡引起的安全問題,Java體系結構采用了一個擴展的內置安全模型,這個模型隨著Java平台的主要版本不斷發展:
1.0版本的基本沙箱
1.1版本的代碼簽名和認證
1.2版本的細粒度訪問控制
Java安全模型側重於保護終端用戶免受從網絡下載的、來自不可靠來源的、惡意程序(以及善意程序中的bug)的侵犯。為此,Java從JDK 1.0開始實現了一套沙箱環境(基本沙箱),沙箱對不可靠的活動進行了限制,程序可以在沙箱的安全邊界內做任何事,但不能進行任何跨越這些邊界的舉動。
但由於最初的沙箱限制過於嚴格,善意(但不可靠)的代碼常常無法進行有效工作。在版本1.1中,得到了改進,引入了基於代碼簽名和認證的信任模式。使得接收終端系統哦就能夠可以確認一系列class文件(在一個jar文件中)已經由某一實體進行了數字簽名(有效,可被信賴),並且在簽名過後,這些class文件沒有改動,這使得終端用戶和系統管理員減少了對某些代碼在沙箱中的限制,但這些代碼必須已由可信任團體進行數字簽名。
雖然版本1.1中發布的安全API包含了對認證的支持,但是實際上,除了提供完全信任和完全不信任策略(換句話說,代碼要麼完全被信任,要麼我完全不被信任)以外,沒有提供其他實際幫助。後面的1.2版本提供的API可以幫助建立細粒度的安全策略。
下面詳細看看基本沙箱、代碼簽名和認證、細粒度訪問控制是如何實現的。
Java沙箱 安全建立在 Java 運行時環境的三個基本方面的基礎上:ByteCode Verifier(字節碼驗證器)、Security Manager(安全管理器)以及 ClassLoader(類裝入器)。
ByteCode Verifier:它確保所下載的代碼被恰當地格式化,字節碼(“Java 虛擬機”指令)沒有違反這種語言或虛擬機的安全限制(無非法數據轉換),沒有執行指針尋址,內部堆棧不能溢出或下溢,以及字節碼指令將擁有正確的類型參數。
Security Manager:它在嘗試執行文件 I/O 和網絡 I/O、創建新的ClassLoader、操作線程或線程組、啟動底層平台(操作系統)上的進程、終止“Java 虛擬機”、將非 Java 庫(本機代碼)裝入到 JVM、完成某種類型的窗口系統操作以及將某種類型的類裝入到 JVM 中時發起運行時訪問控制。
ClassLoader:Java程序(class文件)並不是本地的可執行程序。當運行Java程序時,首先運行JVM(Java虛擬機),然後再把Java class加載到JVM裡頭運行,負責加載Java class的這部分就叫做Class Loader。當運行一個程序的時候,JVM啟動,運行bootstrapclassloader,該ClassLoader加載java核心API(ExtClassLoader和AppClassLoader也在此時被加載),然後調用ExtClassLoader加載擴展API,最後AppClassLoader加載CLASSPATH目錄下定義的Class,這就是一個程序最基本的加載流程。
Java1.1的java.security包中引入的認證策略,認證能幫助用戶通過實現一個沙箱來建立多種安全策略。認證可以使用戶相信由某些團體擔保的class文件具有高度的可信度,並且這些class文件沒有在到達Java虛擬機之前的網絡傳輸中被改變。這樣,就可以簡化沙箱對被認證代碼的限制,針對由不同團體簽名的代碼建立不同的安全限制。
要對一段代碼進行擔保簽名,首先要生成一個公鑰/私鑰對。用戶要公開公鑰,保管私鑰。之後將要簽名的class文件和其他文件放在一個JAR文件中,然後用諸如SDK中的jarsigner 工具對整個JAR文件簽名。這個簽名工具將首先通過單項散列計算,對JAR文件產生一個散列,然後用私鑰對這個散列簽名,並在JAR文件的末尾加上這個簽名,從而完成你對這個JAR文件的數字簽名。
數字簽名的散列計算中大量輸入的是組成JAR文件內容的字節流,產生的是不能包含所有輸入信息的少量數據。這個計算是單向的:從大到小,從輸入到散列。為了加強安全性,要用私鑰進行加密。因為私鑰加密是一個非常費時的過程,我們只對散列進行私鑰的加密。公鑰/私鑰對具有如下特點:在僅給出公鑰的情況下時,想要產生私鑰是非常困難的,而且任何用私鑰加密的代碼都可以用配對的公鑰才能解密。因為不同的輸入可能產生相同的散列,而產生相同散列的概率主要依賴於散列的大小。在實際情況下,散列主要采用64位或128位,這樣的長度要想從不同的輸入中產生一個相同的散列的計算是不可行的。之後將這個加密後的散列值加到同一個JAR文件中,這個JAR文件還包含了你最初產生這個散列的文件。
接受者必須用公鑰對簽名散列進行解密,通過得到的結果和從JAR文件計算得到的散列是否相同,可以驗證一個已經簽名的JAR文件。如果得到的散列值和解密的散列值匹配,則表明了接受者收到的JAR文件確實被發送者所擔保,並且傳輸過程中沒有被修改,這個文件安全性是有保障的。這樣,就可以將這個JAR文件放入安全級別不很嚴格的一般沙箱裡,這個沙箱信任發送者的簽名。
版本1.2的安全體系結構的主要目標之一就是使建立(以簽名代碼為基礎的)細粒度的訪問控制策略的過程更為簡單且更少出錯。版本1.2的安全體系結構中,對應於整個Java應用程序的一個訪問控制策略是由抽象類java.security.Policy的一個子類的單個實例所表示的。
安全策略是一個從描述運行代碼的屬性集合到這段代碼所擁有的權限的映射。在版本1.2的安全體系結構中,描述運行代碼的屬性被總稱為代碼來源。一個代碼來源是由一個java.security.CodeSource對象表示的,這個對象中包含了一個java.net.URL,它表示代碼庫和代表了簽名者的零個或多個證書對象的數組。證書對象是抽象類java.security.cert.Certificate的子類的一個實例,一個Certificate對象抽象表示了從一個人到一個公鑰的綁定,以及另一個為這個綁定作擔保的人(以前提過的證書機構)。CodeSource對象包含了一個Certificate對象的數組,因為同一段代碼可以被多個團體簽名(擔保)。這個簽名通常是從JAR文件中獲得的。
權限是用抽象類java.security.Permission的一個子類的實例表示的。一個Permission對象有三個屬性:類型、名字和可選的操作。
在Policy對象中,每一個CodeSource是和一個或多個Permission對象相關聯的。和一個CodeSource相關聯的Permission對象被封裝在java.security.PermissionCollection的一個子類實例中。
參考資料:
《深入Java虛擬機 第二版》
http://www.Bkjia.com/Article/201210/162438.html