深入Java虛擬機器(3)——安全
因為網路允許多台電腦共用資料和分散式處理,所以它提供了一條入侵電腦系統的潛在途徑,使得其他人可以竊取資訊,改變或破壞資訊,盜取電腦資源等等。為瞭解決由網路引起的安全問題,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