天天看點

java如何實作安全性

原文位址:Inside The JVM Part2: java如何實作安全性

Java通過提供一個”安全沙箱“來保證從網絡或者其他不信任的地方下載下傳并運作的程式不會破壞本地資料,為了確定沙箱是可靠的,java安全模型對體系結構的各方面都進行了考慮。組成java沙箱的基本元件如下:

  • 類裝載器結構
  • class檔案檢驗器
  • 内置于Java虛拟機(及語言)的安全特性
  • 安全管理器及Java API

Java的沙箱安全模型,最重要的優點之一就是這些元件中的類裝載器和安全管理器是可以由使用者定制的。

1、類裝載器體系結構

java沙箱中,類裝載器體系結構是第一道防線,類裝載器體系結構在三個方面對Java的沙箱起作用:

  • 它防止惡意代碼去幹涉善意的代碼。-------通過為由不同的類裝載器裝入的類提供不同的命名空間來實作的,這個命名空間由Java虛拟機維護
  • 它守護了被信任的類庫的邊界。--------通過分别使用不同的類裝載器裝載可靠的包和不可靠的包來實作,參見例1和例2的加深了解。
  • 它将代碼歸入了某類(稱為保護域),該類确定了代碼可以進行哪些操作。

例1:如果某個惡意的類可以成功欺騙Java虛拟機,使Java虛拟機相信它是一個來自Java API的可信任類,那麼,這個惡意的類就可以突破沙箱的阻隔了,讓我們看看java如何阻止這種情況的發生。

Java的類裝載器結構是一個以啟動類裝載器為根的委派鍊,子類裝載器在裝載一個類時首先會請求其雙親類裝載器來裝載,如果雙親裝載器能夠裝載成功,則直接使用該類型,隻有所有雙親類裝載器都裝載失敗的時候,才會根據自定義的方法去裝載該類型,這中工作方式被稱作”雙親委派模式“。

在這種模式下,如果一個自定義的網絡類裝載器試圖從網絡上下載下傳一個和Java API中某個類型同名的類型,如java.lang.Integer時,它将不能成功,因為通過層層委派,這個類型會被啟動類裝載器裝載,而網絡類裝載器将直接使用正确的java.lang.Integer,而沒有機會從網絡上下載下傳并裝載這個惡意的java.lang.Integer。

例2:如果自定義的網絡類裝載器不像例1一樣去替換一個被信任的類,而是在被信任的包中插入一個全新的類型,比如,一個java.lang.Virus的時候,将會發生什麼?

通過層層委派,網絡類裝載器最終會正确裝載這個名為java.lang.Virus的類,暗示了這個類是Java API的一部分,是以,它可以通路java.lang包中被信任類的特殊通路權限(protected),然而,這個情況不會發生,因為java虛拟機隻把彼此通路的特殊權限授予由同一個類裝載器裝載到同一個包中的類型,即隻有同一個”運作時包“的類之間才有特殊通路權限,而 java.lang.Virus和java.lang中其他被信任的類分别有網絡類裝載器和啟動類裝載器裝載,它們不屬于同一個運作時包。

2、class檔案檢驗器

和類裝載器一起,class檔案檢驗器包裝裝載的class檔案内容有正确的内部結構,并且這些class檔案互相間協調一緻,class檔案檢驗器實作的安全目标之一就是程式的健壯性,它必須保證一個class的裝載不會導緻虛拟機的崩潰。

class檔案檢驗器要進行四趟獨立的掃描來完成它的操作。第一趟掃描是在類被裝載時進行的,在這次掃描中,class檔案檢驗器檢查這個class檔案的内部結構,以保證它可以被安全的編譯;第二趟和第三趟掃描是在連接配接過程中進行的,在這兩次掃描中,class檔案檢驗器确認類型資料遵從Java程式設計語言的語義,包括檢驗它所包含的所有位元組碼的完整性;第四趟掃描是在進行動态連接配接的過程中解析符号引用時進行的,在這次掃描中,class檔案檢驗器确認被引用的類、字段以及方法确實存在。

第一趟掃描: class檔案的結構檢查

class檔案檢驗器會檢查每一段被當作類型導入的位元組序列是否符合java class檔案的基本結構,比如是否是以魔數0xCAFEBABE開頭,确認class檔案中聲明的主版本号和次版本号是否在這個java虛拟機的支援範圍内,必須确認這個檔案沒有被删節或者附加一些位元組(通過每個定義長度的地方來确定總體長度)。第一趟掃描的主要目的是保證這個位元組序列正确的定義了一個類型,它必須遵從java class檔案的固定格式,這樣它才能被編譯成在方法區中的(基于實作的)内部資料結構。第二、三、四趟掃描不是在符合class檔案的二進制資料上進行的,而是在方法區中、由實作決定的資料結構上進行的。

第二趟掃描: 類型資料的語義檢查

這次檢查,class檔案檢驗器不需要檢視位元組碼,也不需要檢視和裝載任何其他類型。在這趟掃描中,檢驗器檢視每個組成部分,确認它們是否是其所屬類型的執行個體,它們結構是否正确。比如,方法描述符(它的傳回類型,以及參數的類型和個數)在class檔案中被存儲為一個字元串,這個字元串必須符合特定的上下文無關文法。另外,還會檢查這個類本身是否符合特定的條件,它們是由java程式設計語言規定的。比如,除Object外,所有類都必須要有一個超類,final的類不能被子類化,final方法也沒有被覆寫,檢查常量池中的條目是合法的,而且常量池的所有索引必須指向正确類型的常量池條目。

第三趟掃描: 位元組碼驗證

位元組碼流代表了java的方法,它是由被稱為操作碼的單位元組指令組成的序列,每一個操作碼後都跟着一個或多個操作數。執行位元組碼時,依次執行操作碼,這就在java虛拟機内構成了執行的線程,每一個線程被授予自己的java棧,這個棧是由不同的棧幀構成的,每一個方法調用将獲得一個自己的棧幀----棧幀其實就是一個記憶體片段,其中存儲着局部變量和計算的中間結果,用于存儲中間結果的部分被稱為操作數棧。

位元組碼檢驗器要進行大量的檢查,以確定采用任何路徑在位元組碼流中都得到一個确定的操作碼,確定操作數棧總是包含正确的數值以及正确的類型。它必須保證局部變量在賦予合适的值以前不能被通路,而且類的字段中必須總是被賦予正确類型的值,類的方法被調用時總是傳遞正确數值和類型的參數。位元組碼檢驗器還必須保證每個操作碼都是合法的,即都有合法的操作數,以及對每一個操作碼,合适類型的數值位于局部變量中或是在操作數棧中。這些僅僅是位元組碼檢驗器所做的大量檢驗工作中的一小部分,在整個檢驗過程通過後,它就能保證這個位元組碼流可以被java虛拟機安全的執行。

第四趟掃描: 符合引用的驗證

在動态連接配接過程中,如果包含在一個class檔案中的符号引用被解析時,class檔案檢驗器進行第四趟掃描。在這趟掃描中,java虛拟機将追蹤那些引用-----從被驗證的class檔案到被引用的class檔案,以確定這個引用是正确的。這次掃描可能要裝載新的類。考慮到虛拟機實作上的差别,第四趟掃描可能緊随第三趟掃描發生,也有可能在第三趟掃描之後很久,當位元組碼被執行時才執行。

動态連接配接是一個将符号引用解析為直接引用的過程。當java虛拟機執行位元組碼時,如果它遇到一個操作碼,這個操作碼第一次使用一個指向另一個類的符号引用,那麼虛拟機就必須解析這個符号引用。在解析時,虛拟機執行兩個基本任務:

1)查找被引用的類(如果必要的話就裝載它)

2)将符号引用替換為直接引用,例如指向一個類、字段或方法的指針或偏移量

虛拟機必須記住這個直接引用,這樣當它以後再次遇到同樣的引用時,就可以直接使用,而不需要重新解析該符号引用了。

二進制相容性規則

為了能友善的修改類庫的代碼,java程式設計語言被設計成允許對一個類做多種修改,但并不要求對依賴于它的那些類進行重編譯。java語言規範中列出了使用者可以做的多種改動,這些改動稱為二進制相容性規則。這些規則明确地定義了:在一個類中,哪些可以被修改、增加和删除,而并不破壞這個被修改的類與依賴于它的那些事先已經存在的類之間的二進制相容性。

3、java虛拟機中内置的安全特性

除了四趟掃描之外,java虛拟機在執行位元組碼時還進行其他一些内置的安全機制的操作,這些機制大多數是java的類型安全的基礎,它們作為java程式設計語言保證java程式的健壯性,同樣,它們也是java虛拟機的特性:

  • 類型安全的引用轉換
  • 結構化的記憶體通路(無指針算法)
  • 自動垃圾收集(不必顯式地釋放被配置設定的記憶體)
  • 數組邊界檢查
  • 空引用檢查

4、安全管理器和Java API

java安全模型的前三個部分共同達到了一個目的:保持java虛拟機的執行個體和它正在運作的應用程式的内部完整性,使得它們不被下載下傳的惡意或有漏洞的代碼侵犯。相反,java安全模型的第四個組成部分---安全管理器---則用于保護虛拟機外部資源不被虛拟機内運作的惡意或有漏洞的代碼侵犯。這個安全管理器是一個單獨的對象,在運作的java虛拟機中,它在通路控制---對于外部資源的通路控制---中起中樞作用。

安全管理器定義了沙箱的外部邊界,并且它是可以定制的。

安全管理器中需要了解如下一些概念和類:

  • 預設安全管理器:java.lang.SecurityManager
  • 代碼簽名和認證
  • 政策:java.security.Policy
  • 權限:java.security.Permission
  • 政策檔案
  • 保護域:CodeSource,PersimissionCollection,ProtectionDomain
  • 通路控制器:java.security.AccessController

這部分内容太多,隻能帶過了。

Java安全模型的不足

Java安全模型尚不能解決如下問題:

  • 不斷配置設定記憶體,直到記憶體耗盡
  • 不斷生成線程導緻每件事都慢的不可忍受

以上兩中類型的攻擊被稱作拒絕服務攻擊(DOS)

另一個沒有放入安全模型的領域是關于将權限映射到系統使用者,代碼以這個使用者的名義來運作。這中通路控制在unix系統中較為常見,它基于使用者ID對檔案的通路進行控制