java安全管理器SecurityManager入門

一、文章的目的

  這是一篇對Java安全管理器入門的文章,目的是簡單瞭解什麼是SecurityManager,對管理器進行簡單配置,解決簡單問題。

  比如在閱讀源碼的時候,發現這樣的代碼,想了解是做什麼的:

SecurityManager security = System.getSecurityManager();
if (security != null) {
    security.checkWrite(name);
}

  亦或者在本機運行正常,在服務器運行報錯,想解決問題:

Exception in thread "main" java.security.AccessControlException: access denied (java.lang.RuntimePermission createSecurityManager)
    at java.security.AccessControlContext.checkPermission(AccessControlContext.java:374)
    at java.security.AccessController.checkPermission(AccessController.java:549)
    at java.lang.SecurityManager.checkPermission(SecurityManager.java:532)
    at java.lang.SecurityManager.<init>(SecurityManager.java:282)
    at xia.study._01Thread.ThreadTest.creatThread1(ThreadTest.java:18)
    at xia.study._01Thread.ThreadTest.main(ThreadTest.java:13)

  這時候具備一些SecurityManager的基礎知識還是有必要的。

二、SecurityManager應用場景

  當運行未知的Java程序的時候,該程序可能有惡意代碼(刪除系統文件、重啓系統等),爲了防止運行惡意代碼對系統產生影響,需要對運行的代碼的權限進行控制,這時候就要啓用Java安全管理器。

三、管理器配置文件

3.1 默認配置文件

  默認的安全管理器配置文件是 $JAVA_HOME/jre/lib/security/java.policy,即當未指定配置文件時,將會使用該配置。內容如下:

// Standard extensions get all permissions by default

grant codeBase "file:${{java.ext.dirs}}/*" {
    permission java.security.AllPermission;
};

// default permissions granted to all domains

grant { 
    // Allows any thread to stop itself using the java.lang.Thread.stop()
    // method that takes no argument.
    // Note that this permission is granted by default only to remain
    // backwards compatible.
    // It is strongly recommended that you either remove this permission
    // from this policy file or further restrict it to code sources
    // that you specify, because Thread.stop() is potentially unsafe.
    // See the API specification of java.lang.Thread.stop() for more
        // information.
    permission java.lang.RuntimePermission "stopThread";

    // allows anyone to listen on un-privileged ports
    permission java.net.SocketPermission "localhost:1024-", "listen";

    // "standard" properies that can be read by anyone

    permission java.util.PropertyPermission "java.version", "read";
    permission java.util.PropertyPermission "java.vendor", "read";
    permission java.util.PropertyPermission "java.vendor.url", "read";
    permission java.util.PropertyPermission "java.class.version", "read";
    permission java.util.PropertyPermission "os.name", "read";
    permission java.util.PropertyPermission "os.version", "read";
    permission java.util.PropertyPermission "os.arch", "read";
    permission java.util.PropertyPermission "file.separator", "read";
    permission java.util.PropertyPermission "path.separator", "read";
    permission java.util.PropertyPermission "line.separator", "read";

    permission java.util.PropertyPermission "java.specification.version", "read";
    permission java.util.PropertyPermission "java.specification.vendor", "read";
    permission java.util.PropertyPermission "java.specification.name", "read";

    permission java.util.PropertyPermission "java.vm.specification.version", "read";
    permission java.util.PropertyPermission "java.vm.specification.vendor", "read";
    permission java.util.PropertyPermission "java.vm.specification.name", "read";
    permission java.util.PropertyPermission "java.vm.version", "read";
    permission java.util.PropertyPermission "java.vm.vendor", "read";
    permission java.util.PropertyPermission "java.vm.name", "read";
};

3.2 配置文件詳解

  詳解見第五部分,此處知道有這個配置文件即可。

四、啓動安全管理器

  啓動安全管理有兩種方式,建議使用啓動參數方式。

4.1 啓動參數方式

  啓動程序的時候通過附加參數啓動安全管理器:

-Djava.security.manager

  若要同時指定配置文件的位置那麼示例如下:

-Djava.security.manager -Djava.security.policy="E:/java.policy"

4.2 編碼方式啓動

  也可以通過編碼方式啓動,不過不建議:

System.setSecurityManager(new SecurityManager());

  通過參數啓動,本質上也是通過編碼啓動,不過參數啓動使用靈活,項目啓動源碼如下(sun.misc.Launcher):

// Finally, install a security manager if requested
String s = System.getProperty("java.security.manager");
if (s != null) {
    SecurityManager sm = null;
    if ("".equals(s) || "default".equals(s)) {
        sm = new java.lang.SecurityManager();
    } else {
        try {
            sm = (SecurityManager)loader.loadClass(s).newInstance();
        } catch (IllegalAccessException e) {
        } catch (InstantiationException e) {
        } catch (ClassNotFoundException e) {
        } catch (ClassCastException e) {
        }
    }
    if (sm != null) {
        System.setSecurityManager(sm);
    } else {
        throw new InternalError(
            "Could not create SecurityManager: " + s);
    }
}

  可以發現將會創建一個默認的SecurityManager;

五、配置文件簡單解釋

5.1 配置基本原則

  在啓用安全管理器的時候,配置遵循以下基本原則:

  1. 沒有配置的權限表示沒有。
  2. 只能配置有什麼權限,不能配置禁止做什麼。
  3. 同一種權限可多次配置,取並集。
  4. 統一資源的多種權限可用逗號分割。

5.2 默認配置文件解釋

  第一部分授權:

grant codeBase "file:${{java.ext.dirs}}/*" {
    permission java.security.AllPermission;
};

  授權基於路徑在"file:${{java.ext.dirs}}/*"的class和jar包,所有權限。

  第二部分授權:

grant { 
    permission java.lang.RuntimePermission "stopThread";
    ……   
}

  這是細粒度的授權,對某些資源的操作進行授權。具體不再解釋,可以查看javadoc。如RuntimePermission的可授權操作經查看javadoc如下:

權限目標名稱權限所允許的操作允許此權限所帶來的風險
createClassLoader創建類加載器授予該權限極其危險。能夠實例化自己的類加載器的惡意應用程序可能會在系統中裝載自己的惡意類。這些新加載的類可能被類加載器置於任意保護域中,從而自動將該域的權限授予這些類。
getClassLoader類加載器的獲取(即調用類的類加載器)這將授予攻擊者得到具體類的加載器的權限。這很危險,由於攻擊者能夠訪問類的類加載器,所以攻擊者能夠加載其他可用於該類加載器的類。通常攻擊者不具備這些類的訪問權限。
setContextClassLoader線程使用的上下文類加載器的設置在需要查找可能不存在於系統類加載器中的資源時,系統代碼和擴展部分會使用上下文類加載器。授予 setContextClassLoader 權限將允許代碼改變特定線程(包括系統線程)使用的上下文類加載器。
enableContextClassLoaderOverride線程上下文類加載器方法的子類實現在需要查找可能不存在於系統類加載器中的資源時,系統代碼和擴展部分會使用上下文類加載器。授予 enableContextClassLoaderOverride 權限將允許線程的子類重寫某些方法,這些方法用於得到或設置特定線程的上下文類加載器。
setSecurityManager設置安全管理器(可能會替換現有的)安全管理器是允許應用程序實現安全策略的類。授予 setSecurityManager 權限將通過安裝一個不同的、可能限制更少的安全管理器,來允許代碼改變所用的安全管理器,因此可跳過原有安全管理器所強制執行的某些檢查。
createSecurityManager創建新的安全管理器授予代碼對受保護的、敏感方法的訪問權,可能會泄露有關其他類或執行堆棧的信息。
getenv.{variable name}讀取指定環境變量的值此權限允許代碼讀取特定環境變量的值或確定它是否存在。如果該變量含有機密數據,則這項授權是很危險的。
exitVM.{exit status}暫停帶有指定退出狀態的 Java 虛擬機此權限允許攻擊者通過自動強制暫停虛擬機來發起一次拒絕服務攻擊。注意:自動爲那些從應用程序類路徑加載的全部代碼授予 "exitVM.*" 權限,從而使這些應用程序能夠自行中止。此外,"exitVM" 權限等於 "exitVM.*"。
shutdownHooks虛擬機關閉鉤子 (hook) 的註冊與取消此權限允許攻擊者註冊一個妨礙虛擬機正常關閉的惡意關閉鉤子 (hook)。
setFactory設置由 ServerSocket 或 Socket 使用的套接字工廠,或 URL 使用的流處理程序工廠此權限允許代碼設置套接字、服務器套接字、流處理程序或 RMI 套接字工廠的實際實現。攻擊者可能設置錯誤的實現,從而破壞數據流。
setIOSystem.out、System.in 和 System.err 的設置此權限允許改變標準系統流的值。攻擊者可以改變 System.in 來監視和竊取用戶輸入,或將 System.err 設置爲 "null" OutputStream,從而隱藏發送到 System.err 的所有錯誤信息。
modifyThread修改線程,例如通過調用線程的 interruptstopsuspendresumesetDaemonsetPrioritysetNamesetUncaughtExceptionHandler 方法此權限允許攻擊者修改系統中任意線程的行爲。
stopThread通過調用線程的 stop 方法停止線程如果系統已授予代碼訪問該線程的權限,則此權限允許代碼停止系統中的任何線程。此權限會造成一定的危險,因爲該代碼可能通過中止現有的線程來破壞系統。
modifyThreadGroup修改線程組,例如通過調用 ThreadGroup 的 destroygetParentresumesetDaemonsetMaxPrioritystopsuspend 方法此權限允許攻擊者創建線程組並設置它們的運行優先級。
getProtectionDomain獲取類的 ProtectionDomain此權限允許代碼獲得特定代碼源的安全策略信息。雖然獲得安全策略信息並不足以危及系統安全,但這確實會給攻擊者提供了能夠更好地定位攻擊目標的其他信息,例如本地文件名稱等。
getFileSystemAttributes獲取文件系統屬性此權限允許代碼獲得文件系統信息(如調用者可用的磁盤使用量或磁盤空間)。這存在潛在危險,因爲它泄露了關於系統硬件配置的信息以及一些關於調用者寫入文件特權的信息。
readFileDescriptor讀取文件描述符此權限允許代碼讀取與文件描述符讀取相關的特定文件。如果該文件包含機密數據,則此操作非常危險。
writeFileDescriptor寫入文件描述符此權限允許代碼寫入與描述符相關的特定文件。此權限很危險,因爲它可能允許惡意代碼傳播病毒,或者至少也會填滿整個磁盤。
loadLibrary.{庫名}動態鏈接指定的庫允許 applet 具有加載本機代碼庫的權限是危險的,因爲 Java 安全架構並未設計成可以防止惡意行爲,並且也無法在本機代碼的級別上防止惡意行爲。
accessClassInPackage.{包名}當類加載器調用 SecurityManager 的checkPackageAccess 方法時,通過類加載器的 loadClass 方法訪問指定的包此權限允許代碼訪問它們通常無法訪問的那些包中的類。惡意代碼可能利用這些類幫助它們實現破壞系統安全的企圖。
defineClassInPackage.{包名}當類加載器調用 SecurityManager 的 checkPackageDefinition 方法時,通過類加載器的 defineClass 方法定義指定的包中的類。此權限允許代碼在特定包中定義類。這樣做很危險,因爲具有此權限的惡意代碼可能在受信任的包中定義惡意類,比如 java.securityjava.lang
accessDeclaredMembers訪問類的已聲明成員此權限允許代碼查詢類的公共、受保護、默認(包)訪問和私有的字段和/或方法。儘管代碼可以訪問私有和受保護字段和方法名稱,但它不能訪問私有/受保護字段數據並且不能調用任何私有方法。此外,惡意代碼可能使用該信息來更好地定位攻擊目標。而且,它可以調用類中的任意公共方法和/或訪問公共字段。如果代碼不能用這些方法和字段將對象強制轉換爲類/接口,那麼它通常無法調用這些方法和/或訪問該字段,而這可能很危險。
queuePrintJob打印作業請求的開始這可能向打印機輸出敏感信息,或者只是浪費紙張。
getStackTrace獲取另一個線程的堆棧追蹤信息。此權限允許獲取另一個線程的堆棧追蹤信息。此操作可能允許執行惡意代碼監視線程並發現應用程序中的弱點。
setDefaultUncaughtExceptionHandler在線程由於未捕獲的異常而突然終止時,設置將要使用的默認處理程序此權限允許攻擊者註冊惡意的未捕獲異常處理程序,可能會妨礙線程的終止
Preferences表示得到 java.util.prefs.Preferences 的訪問權所需的權限。java.util.prefs.Preferences 實現了用戶或系統的根,這反過來又允許獲取或更新 Preferences 持久內部存儲中的操作。如果運行此代碼的用戶具有足夠的讀/寫內部存儲的 OS 特權,則此權限就允許用戶讀/寫優先級內部存儲。實際的內部存儲可能位於傳統的文件系統目錄中或註冊表中,這取決於平臺 OS。

5.3 可配置項詳解

  當批量配置的時候,有三種模式:

  • directory/ 表示directory目錄下的所有.class文件,不包括.jar文件
  • directory/* 表示directory目錄下的所有的.class及.jar文件
  • directory/- 表示directory目錄下的所有的.class及.jar文件,包括子目錄

  可以通過${}來引用系統屬性,如:

"file:${{java.ext.dirs}}/*"

六、問題解決

  當出現關於安全管理的報錯的時候,基本有兩種方式來解決。

6.1 取消安全管理器

  一般情況下都是無意啓動安全管理器,所以這時候只需要把安全管理器進行關閉,去掉啓動參數即可。

6.2 增加相應權限

  若因爲沒有權限報錯,則報錯信息中會有請求的權限和請求什麼權限,如下:

Exception in thread "main" java.security.AccessControlException: access denied (java.io.FilePermission E:\pack\a\a.txt write)

  上面例子,請求資源E:\pack\a\a.txt,的FilePermission的寫權限沒有,因此被拒絕。

  也可以開放所有權限:

grant { 
    permission java.security.AllPermission;
};
發佈了37 篇原創文章 · 獲贊 20 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章