程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 解析Java體系結構對信息安全的支持

解析Java體系結構對信息安全的支持

編輯:關於JAVA

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的類的"雙親"即本身這個類加載器;第三種在沒有采用前兩種的情況下使用的默認方式,默認把啟動類加載器作為"雙親"。

在加載過程中,當發出加載請求的時候,加載器首先詢問它的"雙親"――路徑類加載器――來查找並加載這個類,而這個加載器也向它的"雙親"請求加載,一層一層請求上去,直到啟動加載器獲得請求,來查找並加載這個類,如果這個類沒有被加載並且查找不到,返回結果給它的子加載器,由子加載器加載,直到請求返回給原來的加載器,這時還沒有加載成功的話,由網絡類加載器試圖從網絡中尋找並下載,如果還不成功將拋出NoClassDefFoundError異常。

這個過程保證了啟動類加載器可以搶在標准擴展類加載器之前加載類,而標准擴展類加載器又可以搶在路徑類加載器之前加載類,最後才由網絡類加載器加載。比如應用被試圖加載一個帶有惡意代碼的java.lang.String類,因為它本來是Java API的一部分,它們加載到的命名空間可以得到被信任類的特殊訪問權限,但是由於啟動類加載器是最早被加載的,所以java.lang.String只會被啟動類從Java原始的API中加載,而帶有惡意代碼的java.lang.String類不會被加載進來,這樣有效的保護了被信任的類邊界。

類加載器中還包括了一個類型檢查的功能模塊,它負責保證程序的健壯性,它在類型的生命周期中要進行四次檢查。第一次檢查是在加載的時候,主要檢查二進制字節碼的結構,首先格式要滿足Java語言定義的規范,然後要保證將要加載的類字節碼是一組合法的Java指令。第二次檢查是在連接的時候,主要是類型數據的語義檢查,保證字節碼在編譯時候遵守了規范,比如對final類不會派生出子類,也不會重載final的方法;每個類只有一個超類;沒有把基本數據類型強制轉換成其他數據類型。第三次檢查也是在連接的時候,關注於指令的結構,保證指令的操作數類型和值正確,操作數堆棧不會出現上溢出或者下溢出。最後一次檢查在動態連接的時候,主要檢查類型中的符號引用被解析時是否正確。以上的問題都會產生惡意的行為所以必須在運行前進行檢查,而一部分檢查工作會在虛擬機運行字節碼的時候檢查,比如數組越界,對象類型的轉換等等,一旦檢查發現了問題就會拋出異常,使得程序不被執行。

類加載器避免了出現某些懷有敵意的人編寫自己的Java類,而這些類方法中含有跳轉到方法之外的指令,導致虛擬機的崩潰和保密信息被獲取的可能,保證了程序的健壯性,也不會出現替代原有Java API類的惡意代碼被運行的情況,並且類加載器防止了惡意代碼去干涉善意的代碼,守護了被信任的API類庫邊界,確保了代碼可以進行的操作。

安全管理器

安全管理器為Java虛擬機的環境建立了一個起到保護作用的"沙箱",這個"沙箱"為程序提供了一個限制了應用的操作運行環境,保護了虛擬機外部的資源不會被虛擬機內部的惡意代碼破壞。安全管理器的安全策略可以靈活的建立細粒度的訪問控制策略,將不同的資源訪問權限授予不同的代碼單元,這也是Java體系結構安全模型的最大特點和優勢之一。

首先在賦予權限前我們要明確代碼單元的來源,簽名擔保可以使用戶確認文件的來源,並且這些文件在本地虛擬機加載前沒有被修改,只有保證了源代碼來源的可靠性,才能分配相應的操作權限。對class文件或者jar文件的簽名可以用jdk中的jarsigner這個工具。它首先對根據文件內容進行單向散列計算,產生一個散列值,然後把這個散列加到文件後面作為文件的一部分傳遞給用戶,用戶收到文件後重新生成一次散列值,然後跟文件後部的散列值進行比較,如果這兩個散列值相同說明文件內容沒有被改動,雖然不同的文件內容可能生成相同的散列值,但是一般Java采用的是124位散列,這個長度想要從一個不同的輸入產生一個已知散列值的計算是不可行的。僅僅通過散列值是沒有辦法保證代碼的來源的,因為懷有惡意的人完全可以把整個文件和散列值都替換掉,為了防止這種事情發生,必須在發送前用私鑰對散列值進行加密,為什麼不對整個文件進行加密呢,因為jarsigner默認采用的是DES算法,加密本身也是一個費時的過程,我們的目的只是為了保證文件的來源而不是文件本身的保密性,所以只需要對散列值進行加密,用戶收到文件以後用文件提供者的公鑰進行解密來驗證散列值沒有被替換。

明確了代碼來源,並且保證沒有被修改以後,就可以給它分配操作權限。安全策略是一個java.sercurity.Policy的實現類,Policy中主要包含了代碼來源和相應的權限的對應關系信息,安全管理器的check方法將根據Policy對象判斷給導入的代碼賦予什麼樣的操作權限。代碼來源是由java.security.CodeSource表示的,這個對象中包含了一組Certificate對象,說明了為這個代碼文件擔保的簽名。而權限是用java.sercurity.Permission的子類實例來表示的,每一個Permission有三個屬性:類型、名稱和可進行的操作,類型說明了權限控制的資源類型,比如FilePermission說明了是對文件操作的權限,名稱說明了這個權限控制的對象,比如FilePermission的名稱為"/mydoc/order.txt",說明了對文件order.txt的操作權限,可進行的操作屬性說明了這個權限可以進行的操作,比如"read"說明這個FilePermission可以對order.txt進行讀操作。

開發人員可以通過編寫代碼繼承SecurityManager類建立自己的安全管理器來設置安全策略,更方便和常用的方法是通過設置策略文件來實現。這是一個策略文件的例子:

//從mykeys文件中獲得簽名的公鑰
keystore "mykeys"
//由father簽名的代碼賦予讀order.txt文件的權限
grant signedBy "father"{
permission java.io.FilePermission "order.txt","read";
}
//來自{Java_Home}/security/ex/目錄下面的所有jar文件和class文件有讀寫order.txt文件的權限
grant codeBase "file:${Java_Home}/security/ex/*"{
permission java.io.FilePermission "order.txt","read,write";
}

//來自{Java_Home}/security/ex/目錄下面的帶有wife簽名的所有jar文件和class文件有接受、連接和監聽8080端口的權限

grant signedBy "wife" codeBase "file:${Java_Home}/security/ex/*"{
permission java.io.SocketPermission "*:8080","accept,connect,listen";
}
//所有代碼都有讀寫appversion.properties文件的權限
grant {
permission java.io.FilePermission "appversion.properties","read,write";
}

如果一個應用程序沒有指定啟動安全管理器進行干預,它的所有操作不會受到管理器的限制,指定啟動安全管理器有兩種方式,一種是在運行應用程序的時候指定,一種是在代碼中指定。

java -Djava.serciruty.manager myapplication

這裡指定了啟動默認安全管理器,這時候將根據Java默認的全局策略文件來設置安全策略,這個文件是

/JAVA_HOME/lib/security/java.policy。

java -Djava.serciruty.manager -Djava.serciruty.policy=<URL> myapplication

這裡啟動安全管理器,除了默認的全局策略文件外,也會按照Djava.serciruty.policy參數指定的策略文件來設置安全策略,文件指定可以用URL也可以直接用文件名稱。

如果是在代碼中指定,程序通過setSecurityManager方法設置一個java.lang.SecurityManager的實例時候,這時候安全管理器就會開始控制應用程度的操作了。這時當類加載器加載類到虛擬機的時候,安全管理器根據代碼來源和簽名找到相應的權限,然後建立這個類的保護域ProtectionDomain,保護域定義了這個類所有的權限。當應用程序用到這個類並要進行任何可能的不安全操作時,都會向安全管理器請求操作許可,安全管理器中有一系列的check方法來檢查操作是否可行,比如checkRead方法會檢查是否能夠讀取某個文件,checkWrite方法檢查是否能夠寫入某個文件,管理器根據操作請求類型調用相關的check方法,check方法根據類的保護域判斷操作是否可行,如果操作沒有權限的話將拋出一個異常,操作收到這個異常後將不能執行,如果操作可行的話,check方法就順利返回,操作繼續執行。

以上簽名驗證、定義安全策略以及安全管理器檢查操作權限一系列過程,有效的保護了被Java應用使用的外部資源的安全性。要注意的是而由啟動類加載器加載的核心API類是不受這個安全管理器的權限控制,這也是為什麼要用類加載器防止核心API被惡意代碼替換的原因。

Java體系結構提供的安全API

Java體系結構提供了三類主要的安全API:Java 認證和授權服務(Java Authentication and Authorization Service,JAAS)、Java 安全套接字擴展(Java Secure Socket Extension,JSSE)和 Java 加密擴展(Java Cryptography Extension,JCE)。前面已經提到了安全管理器中用到的JAAS,這一節我們提到的是API主要指Java提供的對信息進行加密的Java加密擴展包JCE和Java安全套接字擴展包JSSE, 這些API提供了加密算法可以按照我們的需要,實現對任意數據的加密和解密。

Java加密擴展包JCE提供的是基於密鑰的加密,它通過javax.crypto.Cipher類來實現數據加密合解密,加密解密的對象可以使程序中的數組對象,也可以是通過Java流接口讀出或者寫入的數據。使用Cipher類加密可以選擇多種加密算法、加密模式和填補機制。加密算法JCE中提供了DES、多重DES、PBEWithMD5AndDES、RSA和Blowfish等等。DES是很多機構組織采用的數據加密標准;而多重DES使用多個DES密碼進行多長DES加密,加大了攻擊的難度但是也增加了加密解密過程說花的時間;PBEWithMD5AndDES前面提到過,主要是計算散列,然後對散列進行DES加密,來實現簽名認證;RSA算法是1978年公布的一種分組加密算法,也是現在應用得最廣泛的公鑰密鑰算法;Blowfish是由Bruce Schneier公布的一種加密算法,沒有申請專利,並且公布了實現的代碼,它適合不需要經常更換密鑰的情況。

選擇加密模式是為了對加密數據做進一步調整,從而增加解密難度,模式還可以將分組明文作為流明文進行處理,減少每次處理的數據量,JCE中提供了電子密碼本模式ECB、密碼封裝鏈接模式CBC、密碼反饋模式CFB和輸出反饋模式OFB。電子密碼本模式ECB是最簡單的一種模式,只是對明文每8個字節進行分組,並且一次完成整個明文分組的加密,它適合對二進制數據流進行加密;密碼封裝鏈接模式CBC,把一個8字節的分組作為輸入數據對另一組加密結果進行修正,這樣可以把明文中包含的數據類型進行隱藏,這種模式可以對文本數據進行加密;密碼反饋模式CFB,類似於CBC模式只是實現稍有不同,不同的是CBC模式需要明文分組來進行修正,而CFB需要的數據量小一些,並且長度可以進行調整;輸出反饋模式OFB,適合那種加密數據在傳輸過程中有可能產生變化的情況,產生變化出錯只會對這一位產生影響而不會影響整個分組。JCE可以選擇兩種填補機制PCKS5Padding和NoPadding,前者將對明文進行分組時候,如果字節數不夠8的倍數就進行填充,保證數據的長度為一個完整的分組,而後者不對數據進行填充,當明文進行分組時候,字節數不夠8的倍數會拋出一個異常。

Java安全套接字擴展包JSSE提供的是基於套接字之間傳輸的數據進行加密,它與JCE最大的不同就是數據的加密過程和傳輸過程是不分離的,如果說JAAS讓我們可以識別應用程序提供者並限制他們只能訪問授權使用的那部分系統,那麼JSSE保證了我們應用程序傳輸的數據安全性。JSSE實現了SSL(安全套接字層)的加密,SSL作為HTTPS協議的基礎,提供了在TCP套接字上對數據進行加密的方法,也是基於WEB應用最常用的一種加密方式。使用JSSE API首先我們需要建立SSL環境,SSL服務器端建立密鑰庫存放服務器私鑰和驗證身份的證書,而SSL客戶端建立信任庫驗證信任的證書,密鑰庫和信任庫都是通過JDK中keytool這個工具來進行管理;其次,我們需要從一個 JSSE 套接字工廠而不是直接從 java.net.Socket 類獲得套接字,客戶端代碼從 SSLSocketFactory 獲取套接字,而服務器端代碼從 SSLServerSocketFactory 獲取套接字;通過從這些工廠獲取套接字,我們就可以利用 JSSE 提供程序提供的框架,而不是像 java.net 包允許我們所作的那樣,簡單地創建標准的、不安全的套接字。使用 JSSE,我們可以定義運行任意應用程序協議--包括 HTTP、TCP/IP、FTP,甚至 Telnet--的客戶機與服務器之間的安全套接字連接。

總結:

信息的安全性是計算機領域必須重視和解決的問題,Java體系結構對信息安全的提供靈活而健壯框架,只要我們使用得當就能夠很好的保證信息安全性,降低我們的代價和風險,同時我們也要加強一些其他相關的安全工作,比如保護好我們的私鑰等等,這樣才能保證Java安全框架發揮最大的作用。Java安全框架還有一些不足的地方,比如應用程序不斷分配內存或者新建線程造成拒絕服務、將安全模型與系統用戶進行映射等等,隨著信息技術的不斷發展,信息安全也會面臨越來越大的挑戰,這些都需要Java安全框架更加完善和進一步發展。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved