java安全——类加载器+字节码校验+安全管理器与访问权限
【0】README
0.1)本文文字描述轉自 core java volume 2,旨在學習 java安全 的相關知識;
【1】類加載器
1)java 技術提供了以下3種確保安全的機制(mechanism):
- m1)語言設計特性: (對數組邊界的檢查, 無不受檢查的類型轉換);
- m2)訪問控制機制: 用于控制代碼能夠執行的操作;(比如文件訪問,網絡訪問等);
- m3)代碼簽名: 利用該特性, 代碼的作者就能夠用標準的加密算法來認證java 代碼;
2)類加載器功能: 它可以在將類加載到虛擬機中的時候檢查類的完整性;
【1.1】類加載器
1)java 編譯器會為虛擬機轉換源指令。 虛擬機代碼存儲在以 .class 為擴展名的類文件中, 每個類文件都包含某個類或者接口的定義和代碼實現。 這些類文件必須有一個程序進行解釋,該程序能夠將虛擬機的指令集翻譯成目標機器的機器語言;
Attention)虛擬機只加載程序執行時所需要的類文件。
2)看個荔枝, 假設程序從 MyProgram.class 開始運行, 下面是虛擬機運行的steps:
- step1)虛擬機有一個用于加載類文件的機制, 它使用該機制來加載MyProgram 類文件中的內容;
- step2)如果MyProgram 類擁有類型為另一個類型的域,或者是擁有超類, 那么這些類文件也會被加載。(加載某個類所依賴的所有類的過程稱為類的解析)(干貨——類的解析定義)
- step3)虛擬機執行 MyProgram 中的main方法;
- step4)如果main 方法或者 main 調用的方法要用到更多的類,那么接下來就會加載這些類;
3)類加載機制并非只使用單個的類加載器,每個java程序至少擁有3個類加載器:
- 3.1)引導類加載器: 負責加載系統類(從JAR文件 rt.jar 中進行加載), 它是虛擬機不可分割的一部分,而且通常是用C 語言來實現的;引導類加載器沒有對應的 ClassLoader 對象,例如,該方法: String.class.getClassLoader 將返回 null;
- 3.2)擴展類加載器: 它用于從 jre/lib/ext 目錄加載 標準的擴展。 可以將 JAR 文件放入該目錄,這樣即使沒有任何類路徑,擴展類加載器也可以找到其中的各個類。
- 3.3)系統類加載器(也稱為應用類加載器): 系統類加載器用于加載應用類。 它在由 CLASSPATH 環境變量或者 -classpath 命令行選擇設置的類路徑中的目錄里或者是 JAR/ZIP 文件里查找這些類;
Warning) 如果將 JAR 文件放入 jre/lib/ext 目錄中, 并且在它的類中有一個類需要調用 系統類或者擴展類, 那么就會遇到麻煩。 擴展類加載器并不使用類路徑。在使用擴展目錄來解決類文件的沖突之前, 要牢記這種情況;
Attention) 除了所有已經提到的位置, 還可以從 jre/lib/endorsed 目錄中加載類。這種機制只能用于將某個標準的java 類庫替換為 更新的版本;
【1.1.1】類加載器的層次結構
1)類加載器有一種父子關系: 除了引導類加載器外,每個類加載器都有一個父類加載器。根據規定,類加載器會為它 的父類加載器提供一個機會, 以便加載任何給定的類,并且只有在其父類加載器加載失敗時,它才會加載該給定類;
2)看個荔枝: 當要求系統類加載器加載一個系統類(如, java.util.ArrayList) 時,它首先要求擴展類加載器進行加載, 該擴展類加載器則首先要求 引導類加載器進行加載。引導類加載器查找并加載 rt.jar 中的這個類, 而無需其他類加載器做更多 的搜索;
3)某些程序具有插件架構,如何加載? 如果插件被打包為 JAR 文件, 那就可以直接用 URLClassLoader 類的實例去加載這些類: (干貨——引入URLClassLoader)
3.1)因為在URLClassLoader 構造器中沒有指定父類加載器, 因此 loader 的父親就是 系統類加載器。 下圖展示了這種層次結構:
3.2) 大多數時候,你不必操心類加載的層次結構。 通常,類是由于其他的類需要它而被加載的, 這個過程是透明的;
4)上下文類加載器: 每個線程都有一個對類加載器的引用,稱為上下文類加載器。 (干貨——上下文類加載器)
- 4.1)主線程的上下文類加載器是系統類加載器;
- 4.2)當創建新線程時, 它的上下文件類加載器會被設置成為創建該線程的上下文類加載器;因此,如果你不做任何特殊的操作, 那么所有線程就都將他們的上下文類加載器設置為系統類加載器;
- 4.3)但,我們可以通過下面語句將其設置為任何類加載器:
- 4.4)然后助手方法可以獲取這個上下文類加載器:
【1.1.3】編寫你自己的類加載器
1)如果要編寫自己的類加載器:只需要繼承 ClassLoader 類,然后覆蓋findClass(String className)方法;
2) ClassLoader 超類的 loadClass 方法用于將類的加載操作委托給其父類加載器去進行, 只有當該類尚未加載并且父類加載器也無法加載該類時,才調用 findClass 方法;
3)如果要實現 findClass 方法, 必須做到如下幾點(key):
- k1)為來自本地文件系統或者其他來源的類加載其字節碼;
- k2)調用 ClassLoader 超類的 defineClass 方法, 向虛擬機提供字節碼;
【2】字節碼校驗
1)當類加載器將新加載的java 平臺類的字節碼傳遞給虛擬機時, 這些字節碼首先要接受校驗器的校驗。
2)除了系統類外, 所有的類都要被校驗。下面是校驗器執行的一些檢查(check):
- c1)變量要在使用之前進行初始化;
- c2)方法調用與對象引用類型間要匹配;
- c3)訪問私有數據和方法的規則沒有被違反;
- c4)對本地變量的訪問都落在運行時堆棧內;
- c5)運行時堆棧沒有溢出;
3)in a word, 校驗器的作用: 校驗器總是在防范被故意篡改的類文件, 而不僅僅只是檢查編譯器產生的類文件;
4)看個荔枝(如何建立一個不良的類文件, 字節碼校驗實驗):
step1) 編譯文件;
step2) javap 查看編譯器是如何編譯 fun 方法的:
step3) 用16進制編輯器(vim)打開該.class 文件, 并將指令3的 istore_1 改為 istore_0,也就是說,局部變量0(==m) 被初始化了兩次, 而局部變量1(==n)根本沒有被初始化。
0x10 ,bipush, 將單字節的常量值(-128~127)推送至棧頂
0x3b, istore_0, 將棧頂int型數值存入第一個本地變量
0x3c, istore_1, 將棧頂int型數值存入第二個本地變量所以 fun 方法的字節碼為:10 64 3b 10 63 3c …..(0x64=100,0x63=99)
【3】安全管理器與訪問權限
1) java平臺的第二種安全機制:一旦某個類被加載到虛擬機中, 并由檢驗器檢查之后,java 平臺的第二種安全機制就會啟動, 這個機制就是 安全管理器;
2)安全管理器: 是一個負責控制具體操作是否允許執行的類。安全管理器負責檢查的操作包括以下內容(Content): (干貨——安全管理器定義)
- C1)創建一個新的類加載器;
- C2)退出虛擬機;
- C3)使用反射訪問另一個類的成員;
- C4)訪問本地文件;
- C5)打開 socket 連接;
- C6)啟動打印作業;
- C7)訪問系統剪貼板;
- C8)訪問 AWT 時間隊列;
- C9) 打開一個頂層窗口;
Attention) 在運行 java 應用程序時, 默認的設置是不安裝 安全管理器的, 這樣所有的操作都是允許的;
【3.1】java 平臺安全性
1)JDK 1.0 具有一個非常簡單的安全模型: 即本地類擁有所有權限,而 遠程類只能在沙盒里運行;
2)java se 1.2 開始,有了更靈活的安全機制:他的安全策略建立了代碼來源 和訪問權限集之間的映射關系。
- 2.1)代碼來源:由一個代碼位置和一個證書集指定的。代碼位置指定了代碼來源, 而證書的目的是 由某一方來保障代碼沒有被篡改過;
- 2.2)權限:是指由安全管理器負責檢查的任何屬性。
- 2.3)權限荔枝( 下面這個類允許在 /temp 目錄下讀取和寫入任何文件):
FilePermission p = new FilePermission(“/temp/*”, “read, write”);
3)權限類的層次結構:
4)每個類都有一個保護域:
- 4.1)他是一個用于封裝類的代碼來源和權限集合的對象。 當 SecurityManager 類需要檢查某個權限時, 他要查看當前位于調用堆棧上的所有方法的類,然后他要獲得所有類的保護域, 并且詢問每個保護域, 其權限集合是否允許執行當前正在被檢查的操作。如果所有的域都同意, 那么檢查通過,否則, 就拋出一個 SecurityException 異常;
【3.2】安全策略文件
1)策略管理器: 要讀取相應的策略文件,這些文件包含了將代碼來源映射為權限的指令。 (干貨——策略管理器)
2)看個荔枝(一個典型的策略文件):
- 對上述代碼的分析(Analysis): 該文件給所有下載自 http://www.hosrtmann.com/classes 的代碼授予在 /temp 目錄下讀取和寫入文件的權限。
3) 可以將策略文件 安裝在 標準位置上。 默認情況下, 有兩個位置可以安裝策略文件(position):
- p1) java 平臺主目錄的 java.policy 文件;
- p2) 用戶主目錄的 .java.policy 文件(注意文件名前面的小圓點);
Attention)
- 可以在 java.security 配置文件中 修改這些文件的位置, 默認位置為:
policy.url.1=file:java.home/lib/security/java.policypolicy.url.2=file:{java.home}/.java.policy
- 可以在 java.security 配置文件中 修改這些文件的位置, 默認位置為:
A2) 系統管理員可以修改 java.security 文件, 并可以指定 駐留在另外一臺server 上且用戶無法修改的 策略URL;
4)相比于修改上述的標準文件, 我們更愿意為每個應用程序配置顯式的策略文件, 這樣將權限寫入一個獨立的文件中即可;
4.1)要應用這個策略文件, 可以有兩個選擇selects:
- s1) 一種是在 應用程序的main 方法內部設置系統屬性:
System.setProperty(“java.security.policy”, “MyApp.policy”); - s2)可以像下面這樣啟動 jvm;
java -Djava.security.policy=MyApp.policy MyApp
- s1) 一種是在 應用程序的main 方法內部設置系統屬性:
4.2)在上述例子中, MyApp.policy 文件被添加到了 其他有效的策略中。 如果在命令行中添加了第二個等號, 如: java -Djava.security.policy==MyApp.policy MyApp, 那么應用程序就只使用 指定的策略文件, 而標準策略文件將被忽略;
5)default case 下, java 應用程序是不安裝安全管理器的。 因此, 在安裝安全管理器之前, 看不到策略文件的作用。
- 5.1) 安裝安全管理器: System.setSecurityManager(new SecurityManager()); 添加到 main 方法中;
- 5.2) 也可以添加命令行選項 : java -Djava.security.manager -Djava.security.policy=MyApp.policy MyApp
6)如何描述策略文件的權限:
- 6.1)一個策略文件包含 一系列 grant 項。 每一項都具有以下形式:
- 6.2)代碼來源:包含一個代碼基-code base(如果某一項適用于所有來源的代碼,則代碼基可以省略) 和 值得信賴的用戶特征與證書簽名者的名字;
- 6.3)代碼基可以設定為: codeBase “url” ;
- 6.4)如果URL 以“/”結束,那么它是一個目錄。否則,他將被視為一個 JAR文件的名字。如:
- 6.5)代碼基是一個URL 且總是 以斜杠作為文件分隔符;如: grant codeBase “file:C://myapps/classes/” {…};
7) 權限采用下面的結構: permission className targetName, actionList ; (干貨——權限的結構定義)
- 7.1)類名:是權限類的全稱類名, 且權限類都繼承自 BasicPermission ;
- 7.2)目標名: 是個與權限相關的值, 如,文件權限中的目錄名或者文件名, 或者是 socket 權限中的主機和端口;
- 7.3)操作列表:同樣是與權限相關的,它是一個操作方式的列表,如 read 和 connect 等操作, 用逗號分割;
8)看個權限荔枝:
- 8.1) permission java.io.FilePerssion “/temp”, “read, write”;
- 8.2) socket 權限荔枝: permission java.net.SocketPermission “*.horstmann.com:8000-8999”, “connect”;
- 8.3)允許程序讀取以 java.vm 開頭的所有屬性: permission java.util.PropertyPermission “java.vm.*”, “read”;
- 8.4)可以在策略文件中 使用系統屬性, 其中的 ${property} 標記會被屬性值替代, 如:
permission java.io.FilePermission “${user.home}”, “read, write”; - 8.5) 為了創建平臺無關的策略文件,使用 file.separator 屬性而不是 使用 顯示的 / 或 \ 絕對是個好主意;也可以使用 /作為{file.separator} 的縮寫, 如:
permission java.io.FilePermission "${user.home}${/}-", "read, write";
Attention) policytool: JDK提供了policytool的基礎工具, 可以用它編輯策略文件;
【3.3】定制權限
1)如何把自己的權限類提供給 users, 以使得他們在策略文件中引用這些權限類;
2)實現自己的權限類: 繼承 Permission類, 并提供以下方法(methods):- m1) 帶有兩個String 參數的構造器, 這兩個參數分別是目標和操作列表;
- m2) String getActions();
- m3) boolean equals();
- m4) int hashCode();
- m5) boolean implies(Permission other);
- 最后一個方法是最重要的。 權限有一個排序, 其中更加泛化的權限隱藏了更加具體的權限。 (干貨——最后一個方法implies是最重要的)
3)看個荔枝(請考慮下面的文件權限):
p1 = new FilePermission(“/tmp/-“, “read, write”);
3.1)該權限允許讀寫 /tmp 目錄以及子目錄中的任何文件。該權限隱含了其他更加具體的權限:
p2 = new FilePermission(“/tmp/-“, “read”);
p3 = new FilePermission(“/tmp/aFile”, “read, write”);
p4 = new FilePermission(“/tmp/aDir/”, “write”);對以上權限的分析(Analysis):
- A1) 如果 p1 的目標文件集包含 p2 的目標文件集;
- A2)如果p1 的操作集包含p2 的操作集;
- A3)那么, 文件訪問權限p1 就隱含了另一個文件訪問權限 p2;
4)執行權限檢查: 應該將一個具體的文件權限對象傳遞給 checkPermission 方法:
checkPermission(new FilePermission(filename, “read”));
Attention) 如果你自定義了自己的權限類, 就必須對權限類定義一個合適的隱含法則(通過實現 implies 方法 來實現);【3.4】實現權限類
1)看個荔枝:下面這個JTextArea的子類詢問安全管理器是否準備好了去添加新文本。
class WordCheckTextArea extends JTextArea {public void append(String text){WordCheckPermission p = new WordCheckPermission(text, "insert");SecurityManager manager = System.getSecurityManager();if (manager != null) manager.checkPermission(p);super.append(text);} }- 對上述代碼的分析(Analysis):如果安全管理器賦予了WordCheckPermission權限,那么該文本就可以追加。否則,checkPermission方法就會拋出一個異常。
2)單詞檢查權限有兩個可能的操作: 一個是insert(用于插入特定文本的權限),另一個是avoid(添加不包含某些不良單詞的任何文本的權限)。應該用下面的策略文件運行這個程序:
grant {permission WordCheckPermission "sex,drugs,C++", "avoid"; };- 對上述代碼的分析(Analysis): 這個策略文件賦予的權限,可以插入除不良單詞sex,drugs和C++之外的任何文本。
Attention)當設計WordCheckPermission類時,我們必須特別注意implies方法,下面是控制權限p1是否隱含p2的規則(Rules):
R1)如果p1有avoid操作,p2有insert操作,那么p2的目標必須避開p1中的所有單詞。例如,下面這個權限:
WordCheckPermission “sex,drugs,C++”, “avoid”
隱含了下面這個權限:
WordCheckPermission “Mary had a little lamb”, “insert”R2)如果p1和p2都有avoid操作,那么p2的單詞集合必須包含p1單詞集合中的所有單詞。例如,下面這個權限:
WordCheckPermission “sex,drugs”, “avoid”
隱含了下面這個權限:
WordCheckPermission “sex,drugs,C++”, “avoid”R3)如果p1和p2都有insert操作,那么p1的文本必須包含p2的文本。例如,下面這個權限:
WordCheckPermission “Mary had a little lamb”, “insert”
包含了下面這個權限: WordCheckPermission “a little lamb”, “insert”
Warning)務必要把你的權限類設為public。策略文件加載器不能加載位于引導類路徑之外的可視包的類,并且它會悄悄忽略它無法找到的所有類。 (干貨——務必要把你的權限類設為public)
總結
以上是生活随笔為你收集整理的java安全——类加载器+字节码校验+安全管理器与访问权限的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 寻找伊朗历史上第一款电子游戏
- 下一篇: java.rmi.UnmarshalEx