天天看點

《Java編碼指南:編寫安全可靠程式的75條建議》—— 指南20:使用安全管理器建立一個安全的沙盒

本節書摘來異步社群《java編碼指南:編寫安全可靠程式的75條建議》一書中的第1章,第1.20節,作者:【美】fred long(弗雷德•朗), dhruv mohindra(德魯•莫欣達), robert c.seacord(羅伯特 c.西科德), dean f.sutherland(迪恩 f.薩瑟蘭), david svoboda(大衛•斯沃博達),更多章節内容可以通路雲栖社群“異步社群”公衆号檢視。

根據java api中securitymanager類的文檔[api 2013]:

安全管理器是一個類,它允許應用程式實作一個安全政策。在執行一個可能不安全或敏感的操作前,它允許應用程式确定這個操作是什麼,它是否正在一個安全的允許執行這個操作的上下文中進行嘗試。該應用程式可以允許或禁止該操作。

安全管理器可以與任何java代碼相關聯。

applet的安全管理器拒絕applet最基本特權以外的所有其他特權。如此設計旨在防止無意的系統修改、資訊洩漏和使用者冒名。安全管理器的使用并不局限于用戶端保護。比如tomcat和websphere這樣的web伺服器,使用安全管理器來隔離木馬servlet和惡意的java伺服器頁面(jsp),保護敏感的系統資源不被無意通路。

從指令行運作的java應用程式,可以通過指令行參數标志來設定預設的安全管理器或者自定義的安全管理器。另外,可以通過程式設計方式安裝一個安全管理器。以程式設計方式安裝安全管理器能促使程式建立一個預設的沙盒,這個沙盒會基于目前生效的安全政策來允許或拒絕敏感動作。

從java 2 se平台開始,securitymanager不再是一個抽象類。是以,不再需要顯式覆寫它的方法。以程式設計方式建立和使用安全管理器時,代碼必須具有運作時權限來調用createsecuritymanager(執行個體化securitymanager)和setsecuritymanager(安裝它)。這些權限隻在安全管理器已經安裝時才被檢查。在有些情況下,這很有用,比如在一個虛拟主機上有一個預設的安全管理器,它必須拒絕個人主機以自定義的安全管理器來覆寫預設的安全管理器。

安全管理器與accesscontroller類密切相關,前者是通路控制的中心,後者提供了通路控制算法的實際實作。安全管理器支援以下兩項。

提供向後相容性:老程式通常包含自定義安全性管理器類的實作,因為它最初是抽象類。

定義自定義政策:通過子類化安全管理器來允許定義自定義的安全政策(如多層次、粗粒度或細粒度)。

關于自定義安全管理器與預設安全管理器的實作和使用,java安全架構規範(java security architecture specification) [securityspec 2010]中是這麼聲明的:

我們鼓勵在應用程式代碼中使用accesscontroller類,而定制安全管理器(通過子類化)應該是最後的手段,應當十分小心。此外,一個定制的安全管理器,如在調用标準安全檢查前總是檢查目前時間,在合适的時候可以而且應該使用accesscontroller所提供的算法。

許多java se api在執行敏感操作前,都會預設執行安全管理器檢查。例如,java.io.fileinputstream的構造函數,如果調用者沒有足夠的讀取檔案的權限,它就會抛出securityexception異常。因為securityexception安全異常是runtimeexception運作時異常的一個子類,是以一些api方法(如java.io.filereader類的方法)的聲明可能缺乏列出securityexception的throws語句。要避免依賴于沒有在api方法文檔中指定的安全管理器檢查是否存在。

下面的違規代碼示例沒有從指令行安裝任何安全管理器。是以,程式運作時啟用了所有權限,也就是說,沒有安全管理器來防止程式可能執行的任何邪惡動作。

java -djava.security.manager -djava.security.policy=policyurl \

   localjavaapp<code>`</code>

指令行參數标志可以指定一個自定義的安全管理器,其政策被全局執行。使用-djava.security.manager标志,如下所示:

java -djava.security.manager \

   -djava.security.policy==policyurl \

appletviewer自動安裝了一個帶有标準政策檔案的安全管理器,并且使用-j标志指定了附加的政策檔案。

try {

 system.setsecuritymanager(null);

} catch (securityexception se) {

 // cannot set security manager, log to file

}<code>`</code>

一個實施合理安全政策的活動的securitymanager能阻止系統令其失效,讓這段代碼抛出securityexception異常。

下面的合規解決方案對預設安全管理器進行了執行個體化和設定。

char password[] = / 初始化 /

 system.setsecuritymanager(

  new customsecuritymanager("password here")

 );

 // cannot set security manager, log appropriately

這段代碼執行後,執行安全檢查的api将使用這個自定義安全管理器。如前所述,隻有當預設安全管理器缺少所需要的功能時,才考慮安裝自定義安全管理器。

java的安全性從根本上取決于安全管理器的存在。當其不存在時,敏感動作可以無限制地執行。

在運作時程式設計檢測安全管理器的存在與否是很簡單的。靜态分析可以解決此類代碼的存在與否,如果該代碼被執行,它将試圖安裝一個安全管理器。在某些情況下,可以檢查安全管理器是否安裝得夠早、是否指定了所需的屬性,或者是否可以保證會被安裝,但通常情況下是不可判定的。

當已知預設的全局安全管理器總是會從指令行安裝時,對setsecurity manager()方法的調用在受控環境中可能會被忽略。這很難實施,如果環境配置不正确,會出現漏洞。