1. 程式人生 > >Java安全管理器SecurityManager

Java安全管理器SecurityManager

安全管理器是一個允許應用程式實現安全策略的類。它允許應用程式在執行一個可能不安全或敏感的操作前確定該操作是什麼,以及是否是在允許執行該操作的安全上下文中執行它。應用程式可以允許或不允許該操作。

SecurityManager 類包含了很多名稱以單詞 check 開頭的方法。Java 庫中的各種方法在執行某些潛在的敏感操作前可以呼叫這些方法。對 check 方法的典型呼叫如下:

     SecurityManager security = System.getSecurityManager();

     if (security != null) {

         security.checkXXX

(argument,  . . . );

     }

因此,安全管理器通過丟擲異常來提供阻止操作完成的機會。如果允許執行該操作,則安全管理器例程只是簡單地返回,但如果不允許執行該操作,則丟擲一個 SecurityException。該約定的唯一例外是 checkTopLevelWindow,它返回 boolean 值。

當前的安全管理器由 System 類中的 setSecurityManager 方法設定。當前的安全管理器由getSecurityManager 方法獲得。

   AccessController.checkPermission(perm);

如果允許訪問請求,則安靜地返回 checkPermission。如果拒絕訪問請求,則丟擲SecurityException。

從 Java 2 SDK v1.2 開始,SecurityManager 中其他所有 check 方法的預設實現都是呼叫SecurityManager checkPermission 方法來確定呼叫執行緒是否具有執行所請求操作的許可權。

注意,只帶有單個許可權引數的 checkPermission 方法總是在當前執行的執行緒上下文中執行安全檢查。有時,應該在給定上下文中進行的安全檢查實際上需要在不同 的上下文(例如,在一個輔助執行緒中)中進行。Java 為這種情況提供了包含有上下文引數的 getSecurityContext方法和checkPermission方法。getSecurityContext 方法返回當前呼叫上下文的一個“快照”(預設的實現返回一個 AccessControlContext 物件)。下面是一個示例呼叫:

   Object context = null;

   SecurityManager sm = System.getSecurityManager();

   if (sm != null) context = sm.getSecurityContext();

checkPermission 方法使用一個上下文物件,以及根據該上下文而不是當前執行執行緒的上下文作出訪問決策的許可權。因此另一個上下文中的程式碼可以呼叫此方法,傳遞許可權和以前儲存的上下文物件。下面是一個示例呼叫,它使用了以前示例中獲得的 SecurityManager sm:

   if (sm != null) sm.checkPermission(permission, context);

許可權分為以下類別:檔案、套接字、網路、安全性、執行時、屬性、AWT、反射和可序列化。管理各種許可權類別的類是java.io.FilePermission、java.net.SocketPermission、java.net.NetPermission、java.security.SecurityPermission、java.lang.RuntimePermission、java.util.PropertyPermission、java.awt.AWTPermission、java.lang.reflect.ReflectPermission和 java.io.SerializablePermission。

除前兩個(FilePermission 和 SocketPermission)類以外的所有類都是java.security.BasicPermission 的子類,而 java.security.BasicPermission 類又是頂級許可權類java.security.Permission 的抽象子類。BasicPermission 定義了所有許可權所需的功能,這些功能的名稱遵從分層屬性命名慣例(例如“exitVM”、“setFactory”、“queuePrintJob”等等)。在名稱的末尾可能出現一個星號,前面是“.”或星號,這表示萬用字元匹配。例如:“a.*”、“*”是有效的,而“*a”或“a*b”是無效的。

FilePermission 和 SocketPermission 是頂級許可權類 (java.security.Permission) 的子類。像這些命名語法比 BasicPermission 所用的語法更為複雜的類都直接是 Permission 的子類,而不是BasicPermission 的子類。例如,對於 java.io.FilePermission 物件而言,許可權名就是檔案(或目錄)的路徑名。

某些許可權類具有一個“動作”列表,告知允許物件所執行的動作。例如,對於java.io.FilePermission 物件,動作列表(如“讀、寫”)指定了允許對指定檔案(或指定目錄中的檔案)執行哪些動作。

其他許可權類是“指定的”許可權 - 有名稱但沒有動作列表的類;您也許有指定的許可權,也許沒有。

注:還有一個暗指所有許可權的 java.security.AllPermission 許可權。該許可權是為了簡化系統管理員的工作而存在的,因為管理員可能需要執行很多需要所有(或許多)許可權的任務。

二、啟動/關閉SecurityManager

1. 指定 -Djava.security.manager

當我們執行一個程式,我們可以指定JVM命令 -Djava.security.manager 使SecurityManager執行。

java -Djava.security.manager <class_name>

這是開啟SecurityManager最常見的方式。java.security.manager是一個系統屬性,您可以使用System.getProperty(“java.security.manager”)檢查該系統屬性是否被設定。

在這裡,你可能會認為,我們可以使用System.setProperty(“java.security.manager”)開啟SecurityManager,但是並不能這麼設定。因為先前我們提到,這個系統屬性是在當JVM啟動時進行檢查的。如果我們用程式手動設定該屬性,並不能奏效,因為JVM已經啟動了,已經過了檢查系統屬性的步驟了。

2. 通過程式開啟SecurityManager

現在,如果我們真的想通過我們的程式開啟SecurityManager,我們也能做到。 System類中有一個叫 setSecurityManager() 的方法可以做到這一點。這個方法的引數是一個SecurityManager例項。

SecurityManager sm=new SecurityManager();

System.setSecurityManager(sm);

通過這個,我們可以開啟SecurityManager.。如果之後我們想要關閉SecurityManager, 我們該怎麼做? 下面的程式碼能做到嗎?

SecurityManager sm=System.getSecurityManager();

if(sm!=null){

    System.setSecurityManager(null);

}

上面的程式碼只有你在位於${JDK_HOME}/jre/lib/security目錄下或者其他指定目錄下的java.policy檔案中指定了一個許可權才會奏效。 這個許可權是:

permission java.lang.RuntimePermission "setSecurityManager";

上面的一行將被用來允許程式碼設定SecurityManager

三、Java policy檔案

預設載入的策略檔案的位置,我們可以從${JDK_HOME}/jre/lib/security目錄下的java.security檔案中看到:

# The default is to have a single system-wide policy file,

# and a policy file in the user's home directory.

policy.url.1=file:${java.home}/lib/security/java.policy

policy.url.2=file:${user.home}/.java.policy

也可以在啟動的時候指定其他位置的策略檔案:

java -Djava.security.manager -Djava.security.policy=someURL SomeApp

四、許可權分類

JDK8有下面這麼多的許可權類,每個許可權類控制某一方面的相關許可權,而每個許可權類可能又會有不同的動作,所以控制範圍是方方面面的。

 

 

 

五、自定義SecurityManager

Java程式碼  收藏程式碼
  1. public class SecurityManagerTest {  
  2.     private static class MySecurityManager extends SecurityManager {  
  3.         @Override  
  4.         public void checkRead(String file) {  
  5.             if ("java.policy".contains(file)) {  
  6.                 throw new AccessControlException("cannot read file:" + file);  
  7.             }  
  8.             super.checkRead(file);  
  9.         }  
  10.     }  
  11.     public static void main(String[] args) throws FileNotFoundException {  
  12.         //install  
  13.         System.setSecurityManager(new MySecurityManager());  
  14.         //read  
  15.         InputStream in = new FileInputStream(new File("java.policy"));  
  16.         //uninstall  
  17.         SecurityManager sm = System.getSecurityManager();  
  18.         if (sm != null) {  
  19.             System.setSecurityManager(null);  
  20.         }  
  21.     }  
  22. }  

輸出:

Exception in thread "main" java.security.AccessControlException: cannot read file:java.policy

    at grucee.test.SecurityManagerTest$MySecurityManager.checkRead(SecurityManagerTest.java:17)

    at java.io.FileInputStream.<init>(FileInputStream.java:121)

    at grucee.test.SecurityManagerTest.main(SecurityManagerTest.java:29)

六、SecurityManager在tomcat中的使用(翻譯)

1.背景

Java SecurityManager可以讓web瀏覽器在它自己的沙箱中執行applet,從而防止不可信程式碼訪問本地檔案系統中的檔案、連線applet載入主機之外的主機,等等。同樣,SecurityManager可以阻止你的瀏覽器執行不安全的applet;執行tomcat的時候,使用SecurityManager可以保護你的機器不受惡意Servlet、JSP,甚至是疏忽的錯誤的影響。

想象一下一個有許可權釋出JSP頁面的人員,由於輸出在他們的JSP頁面中包括了下面這句:

<% System.exit(1); %>

Tomcat每次執行這個JSP,tomcat都會退出。使用Java SecurityManager是系統管理員保證伺服器安全、可靠地另外一種防線。

無論如何,執行SecurityManger比不執行要好很多。

2.Permissions

Permission類用來定義tomcat載入的類應該有什麼許可權。JDK有很多標準的Permission類,並且你可以建立自己的Permission類。Tomcat兩種技術都有使用。

標準Permissions

這裡只是適用於tomcat的標準SecurityManager Permission類的總結:

  • java.util.PropertyPermission - Controls read/write access to JVM properties such as java.home.
  • java.lang.RuntimePermission - Controls use of some System/Runtime functions like exit() and exec(). Also control the package access/definition.
  • java.io.FilePermission - Controls read/write/execute access to files and directories.
  • java.net.SocketPermission - Controls use of network sockets.
  • java.net.NetPermission - Controls use of multicast network connections.
  • java.lang.reflect.ReflectPermission - Controls use of reflection to do class introspection.
  • java.security.SecurityPermission - Controls access to Security methods.
  • java.security.AllPermission - Allows access to all permissions, just as if you were running Tomcat without a SecurityManager.

Tomcat自定義Permissions

Tomcat使用了 org.apache.naming.JndiPermission的自定義許可權類。該許可權控制基於JNDI命名的資源的訪問。許可權的名稱是JNDI名,無動作。可以使用結尾的*來進行模糊匹配,例如

permission  org.apache.naming.JndiPermission  "jndi://localhost/examples/*";

每個部署的Web應用都會動態生成這樣的許可權實體,以允許讀取自己的靜態資源,而不允許讀取其他的檔案(除非顯示賦予這些檔案的許可權)。

而且,Tocat總是動態建立下面的檔案許可權:

permission java.io.FilePermission "** your application context**", "read";

permission java.io.FilePermission

  "** application working directory**", "read,write";

permission java.io.FilePermission

  "** application working directory**/-", "read,write,delete";

這裡的**your application context”等同於應用程式部署的目錄(或WAR檔案),**application working directory**是Servlet規範要求的臨時目錄。

3.使用SecurityManager配置Tomcat

策略檔案格式

Java SecurityManager實現的安全策略檔案配置在$CATALINA_BASE/conf/catalina.policy。該檔案完全替換JDK目錄中的java.policy檔案。catalina.policy檔案可以手動編輯,也可以使用Java 1.2及以上自帶的policytool工具。

catalina.policy檔案中的實體使用標準java.policy檔案的格式,如下:

// Example policy file entry

grant [signedBy <signer>,] [codeBase <code source>] {

  permission  <class>  [<name> [, <action list>]];

};

signedBy和codeBase實體在賦予許可權的時候是可選的。註釋以//開頭並至行尾。codeBase以URL的格式,檔案URL可以使用${java.home}和${catalina.home} 屬性(將來會擴充套件到JAVA_HOME,CATALINA_HOME以及CATALINA_BASE環境變數)。

4.使用SecurityManager啟動Tomcat

$CATALINA_HOME/bin/catalina.sh start -security    (Unix)

%CATALINA_HOME%\bin\catalina start -security      (Windows)

一旦你配置了catalina.policy檔案,Tomcat可以使用”-security”選項啟動SecurityManager。