2020-03-25-Java安全模型(AccessController)(轉)


layout: post
title: Java安全模型(AccessController)(轉)
categories: [Java]
description: Java安全模型(AccessController)
keywords: Java, AccessController


轉載自 Java 安全模型介紹

Java 安全模型

在 Java 中將執行程序分成本地和遠程兩種,本地代碼默認視為可信任的,而遠程代碼則被看作是不受信的。對于授信的本地代碼,可以訪問一切本地資源。而對于非授信的遠程代碼在早期的 Java 實現中,安全依賴于沙箱 (Sandbox) 機制。沙箱機制就是將 Java 代碼限定在虛擬機 (JVM) 特定的運行范圍中,并且嚴格限制代碼對本地系統的資源訪問,通過這樣的措施來保證對遠程代碼的有效隔離,防止對本地系統造成破壞。如圖所示

image-20200325150232862.png

但如此嚴格的安全機制也給程序的功能擴展帶來障礙,比如當用戶希望遠程代碼訪問本地系統的文件時候,就無法實現。因此在后續的 Java1.1 版本中,針對安全機制做了改進,增加了安全策略,允許用戶指定代碼對本地資源的訪問權限。如圖所示

image-20200325150307744.png

在 Java1.2 版本中,再次改進了安全機制,增加了代碼簽名。不論本地代碼或是遠程代碼,都會按照用戶的安全策略設定,由類加載器加載到虛擬機中權限不同的運行空間,來實現差異化的代碼執行權限控制。如圖所示

image-20200325150337220.png

當前最新的安全機制實現,則引入了域 (Domain) 的概念。虛擬機會把所有代碼加載到不同的系統域和應用域,系統域部分專門負責與關鍵資源進行交互,而各個應用域部分則通過系統域的部分代理來對各種需要的資源進行訪問。虛擬機中不同的受保護域 (Protected Domain),對應不一樣的權限 (Permission)。存在于不同域中的類文件就具有了當前域的全部權限,如圖所示

image-20200325150409427.png

以上提到的都是基本的 Java 安全模型概念,在應用開發中還有一些關于安全的復雜用法,其中最常用到的 API 就是 doPrivileged。doPrivileged 方法能夠使一段受信任代碼獲得更大的權限,甚至比調用它的應用程序還要多,可做到臨時訪問更多的資源。有時候這是非常必要的,可以應付一些特殊的應用場景。例如,應用程序可能無法直接訪問某些系統資源,但這樣的應用程序必須得到這些資源才能夠完成功能。針對這種情況,Java SDK 給域提供了 doPrivileged 方法,讓程序突破當前域權限限制,臨時擴大訪問權限。下面內容會詳細講解一下安全相關的方法使用。

Java 安全控制實現

Java SDK 中與安全相關的類和接口都放在 java.security 包中,其中既包括訪問控制配置及細粒度訪問控制框架的實現,還包括簽名和加解密的實現。本文中涉及到的安全訪問控制主要與安全包中訪問控制框架相關,這里面最常用的就是 AccessController 類。通過下圖的描述,您可以了解 ACC(Access Contorller) 機制是如何運作的。

在某一個線程的調用棧中,當 AccessController 的 checkPermission 方法被最近的調用程序(例如 A 類中的方法)調用時,對于程序要求的所有訪問權限,ACC 決定是否授權的基本算法如下:

  1. 如果調用鏈中的某個調用程序沒有所需的權限,將拋出 AccessControlException;
  2. 若是滿足以下情況即被授予權限:
    • 調用程序訪問另一個有該權限域里程序的方法,并且此方法標記為有訪問“特權”;
    • 調用程序所調用(直接或間接)的后續對象都有上述權限。

在上面例子的調用鏈中,假定 E 域和 F 域不具備 X 權限 (permission),而在 C.class 對應的 G 域具有 X 權限,同時 C 使用 X 權限的對外接口 Y 方法是通過 doPrivilege 方式實現。那么,B.class A.class 調用 Y 方法就都具備 X 權限。如果 Y 方法沒有標注 doPrivilege,那么對 Y 方法的調用就不具備 X 權限。

還有一種特殊的情況,就是訪問控制上下文的繼承問題。當一個線程創建另一個新線程時,會同時創建新的堆棧。如果創建新線程時沒有保留當前的安全上下文,也就是線程相關的安全信息,則新線程調用 AccessController.checkPermission 檢驗權限時,安全訪問控制機制只會根據新線程的上下文來決定安全性問題,而不會考慮其父線程的相應權限。這個清除堆棧的做法本身并不會給系統帶來安全隱患,但它會使源代碼,尤其是系統代碼的編寫容易出現錯誤。例如,對安全框架實現不熟悉編程人員可能會很自然地認為,子線程執行的信任代碼繼承了父線程執行的不可信任代碼的安全限制特性。當從子線程內訪問受控制的資源時,如果父線程的安全上下文信息并未保存,就會導致意外的安全漏洞。因為丟失的父線程中安全限制數據會使子線程將資源傳遞給一些不可信任的代碼。因此,在創建新線程時,必須確保利用父線程創建,或利用其他形式創建代碼。總之,要保證讓子線程自動繼承父線程的安全性上下文,這樣子線程中的后續 AccessController.checkPermission 調用就會考慮所繼承的父線程的安全特性。

需要注意是 AccessController 類的 checkPermission 方法將在當前執行線程的上下文,包括繼承的上下文中進行安全檢查。當這種安全檢查只能在不同的上下文中進行時就會出現問題。意即,本應在一個線程上下文內部進行的安全檢查,有時卻需要在不同上下文中進行。例如,當一個線程將某個事件傳給另一個線程時,如果所請求的事件服務要求訪問某種安全受控資源,則為其請求事件服務的第二個線程將沒有事件產生源線程相應的上下文來完成所需的訪問控制決策。為解決這樣的問題,Java 在 AccessController 類中提供了 getContext 方法和 AccessControlContext 對象。通過 getContext 方法可獲取當前調用上下文的“快照 (snapshot)”,然后將其存放到返回的 AccessControlContext 對象中。調用的樣例程序如下所示:

AccessControlContext acc = AccessController.getContext();

getContext 方法將當前上下文的快照信息捕獲,然后執行程序就可以通過檢查前后不同上下文中的信息,即比較快照上下文信息與本上下文信息,然后來做出對受控資源訪問控制的決策。上面問題就可以如下方式來解決:

當前一個線程把某個請求事件傳給第二個線程時,同時捕獲其上下文信息并將這些信息提供給后一個線程。略有不同的是,AccessControlContext 類本身的 checkPermission 方法可根據它自身攜帶的上下文信息來決定訪問控制,而不是根據當前正在執行的線程上下文。因此必要時,后一個線程可直接通過調用前一個線程上下文快照本身的權限檢查方法來執行相應的安全檢查。如下:

acc.checkPermission(permission)

上述方法調用等同于在前一個線程的上下文中執行相同的安全檢查,盡管訪問控制檢查實際上是在后一個線程中完成的。

安全控制使用的代碼實例

上面關于安全控制使用的描述還是比較晦澀,下面將通過一個代碼示例進行說明。

在 Eclipse 開發環境中建立兩個不同工程:projectX 和 projectY。我們會給 projectX 工程中的 bin 目錄賦予寫文件的權限,換句話說就是允許所有存在于此目錄中的 class 文件可以自由的在 bin 目錄中進行文件寫操作。然后,我們會在 projectY 工程中調用 projectX 工程中的一個文件操作工具類。這個工具類提供兩種類型接口,一種是特權訪問方式,另外一種是普通訪問方式。由于在 projectY 工程中的文件是不具備在 projectX 工程中 bin 目錄的任何寫權限,所以通過三種不同訪問方式的調用結果,我們就可以很清楚地了解到 Java 中安全控制該如何使用。

假定 ProjectX 的項目路徑為 D:\workspace\projectX\

package learn.java.security; 

import java.io.File; 
import java.io.IOException; 
import java.security.AccessControlException; 
import java.security.AccessController; 
import java.security.PrivilegedAction; 

public class FileUtil { 
  // 工程 A 執行文件的路徑 
  private final static String FOLDER_PATH = "D:\\workspace\\projectX\\bin"; 
  
  public static void makeFile(String fileName) { 
    try { 
      // 嘗試在工程 A 執行文件的路徑中創建一個新文件
      File fs = new File(FOLDER_PATH + "\\" + fileName); 
      fs.createNewFile(); 
    } catch (AccessControlException e) { 
      e.printStackTrace(); 
    } catch (IOException e) { 
      e.printStackTrace(); 
    } 
  } 
  
  public static void doPrivilegedAction(final String fileName) { 
    // 用特權訪問方式創建文件
    AccessController.doPrivileged(new PrivilegedAction<``String``>() { 
      @Override 
      public String run() { 
        makeFile(fileName); 
        return null; 
      } 
    }); 
  } 
}  

假定 ProjectY 的項目路徑為 D:\workspace\projectY\

package demo.security; 

import java.io.File; 
import java.io.IOException; 
import java.security.AccessControlException; 

import learn.java.security.FileUtil; 

public class DemoDoPrivilege { 
  public static void main(String[] args) { 
    System.out.println("***************************************"); 
    System.out.println("I will show AccessControl functionality..."); 
    
    System.out.println("Preparation step : turn on system permission check..."); 
    // 打開系統安全權限檢查開關
    System.setSecurityManager(new SecurityManager()); 
    System.out.println(); 
    
    System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); 
    System.out.println("Create a new file named temp1.txt via privileged action ..."); 
    // 用特權訪問方式在工程 A 執行文件路徑中創建 temp1.txt 文件
    FileUtil.doPrivilegedAction("temp1.txt"); 
    System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); 
    System.out.println();
    System.out.println("http://///////////////////////////////////////"); 
    System.out.println("Create a new file named temp2.txt via File ..."); 
    try { 
      // 用普通文件操作方式在工程 A 執行文件路徑中創建 temp2.txt 文件
      File fs = new File("D:\\workspace\\projectX\\bin\\temp2.txt"); 
      fs.createNewFile(); 
    } catch (IOException e) { 
      e.printStackTrace(); 
    } catch (AccessControlException e1) { 
      e1.printStackTrace(); 
    } 
    System.out.println("http://///////////////////////////////////////"); 
    System.out.println(); 
    
    System.out.println("-----------------------------------------"); 
    System.out.println("create a new file named temp3.txt via FileUtil ..."); 
    // 直接調用普通接口方式在工程 A 執行文件路徑中創建 temp3.txt 文件
    FileUtil.makeFile("temp3.txt"); 
    System.out.println("-----------------------------------------"); 
    System.out.println(); 
    System.out.println("***************************************"); 
  } 
}

應用的安全訪問控制策略文件 (MyPolicy.txt) 如下 , 假定安全策略文件放于 projectY 工程的根目錄下:

// 授權工程 A 執行文件路徑中文件在本目錄中的寫文件權限
grant codebase "file:/D:/workspace/projectX/bin"
{
    permission java.io.FilePermission
        "D:\\workspace\\projectX\\bin\\*", "write"; 
};

下面就可以運行程序了,您可以選擇在 Eclipse 開發環境中直接運行,也可以通過命令行來執行。命令行執行如下所示,假定當前執行目錄就是 projectY 的根目錄。

java -Djava.security.policy=.\\MyPolicy.txt -classpath D:\workspace\projectY\bin;D:\workspace\projectX\bin demo.security.DemoDoPrivilege

執行結果如下:

image-20200325152712108.png

通過程序打印結果來看,當往 projectX 工程中 bin 目錄創建 temp1.txt,temp2.txt,temp3.txt 文件時候,除了通過特權訪問方式可以創建成功外,通過普通接口訪問或者直接文件操作方式都會失敗,失敗的原因都是沒有通過權限檢查。

對照前文所描述的權限檢查規則,用一句話總結就是想訪問安全資源,要么調用鏈上權限齊全,要么就要用特權。特權訪問機制實際上就是給應用開后門的,使用上需要小心,所以這也給代碼實現帶來新的考慮,開放范圍一定要限定好,否則可能留下安全隱患。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,443評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,530評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,407評論 0 375
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,981評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,759評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,204評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,263評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,415評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,955評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,782評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,983評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,528評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,222評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,650評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,892評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,675評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,967評論 2 374

推薦閱讀更多精彩內容

  • 作為一種誕生于互聯網興起時代的語言,Java 從一開始就帶有安全上的考慮,如何保證通過互聯網下載到本地的 Java...
    barry_di閱讀 557評論 0 2
  • Java安全模型側重于保護終端用戶免受從網絡下載的、來自不可靠來源的、惡意程序(以及善意程序中的bug)的侵犯。為...
    strang_閱讀 491評論 0 2
  • 一:java概述:1,JDK:Java Development Kit,java的開發和運行環境,java的開發工...
    ZaneInTheSun閱讀 2,676評論 0 11
  • 1、面向對象的特征有哪些方面 1.抽象:抽象就是忽略一個主題中與當前目標無關的那些方面,以便更充分地注意與當前目標...
    michaelgong閱讀 834評論 0 1
  • 1. cpu通過時間片分配算法來循環執行任務,當前任務執行一個時間片后會切換到下一任務。但是,再切換之前會保存上一...
    冰與河豚魚閱讀 676評論 0 0