這是一篇對Java安全管理器入門的文章,目的是簡單瞭解什麼是SecurityManager,對管理器進行簡單配置,解決簡單問題。java
好比在閱讀源碼的時候,發現這樣的代碼,想了解是作什麼的:安全
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的基礎知識仍是有必要的。架構
當運行未知的Java程序的時候,該程序可能有惡意代碼(刪除系統文件、重啓系統等),爲了防止運行惡意代碼對系統產生影響,須要對運行的代碼的權限進行控制,這時候就要啓用Java安全管理器。併發
默認的安全管理器配置文件是 $JAVA_HOME/jre/lib/security/java.policy,即當未指定配置文件時,將會使用該配置。內容以下:app
// 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"; };
詳解見第五部分,此處知道有這個配置文件便可。dom
啓動安全管理有兩種方式,建議使用啓動參數方式。ide
啓動程序的時候經過附加參數啓動安全管理器:this
-Djava.security.manager
若要同時指定配置文件的位置那麼示例以下:編碼
-Djava.security.manager -Djava.security.policy="E:/java.policy"
也能夠經過編碼方式啓動,不過不建議:
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;
在啓用安全管理器的時候,配置遵循如下基本原則:
第一部分受權:
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 套接字工廠的實際實現。攻擊者可能設置錯誤的實現,從而破壞數據流。 |
setIO | System.out、System.in 和 System.err 的設置 | 此權限容許改變標準系統流的值。攻擊者能夠改變 System.in 來監視和竊取用戶輸入,或將 System.err 設置爲 "null" OutputStream,從而隱藏發送到 System.err 的全部錯誤信息。 |
modifyThread | 修改線程,例如經過調用線程的 interrupt、stop、suspend、resume、setDaemon、setPriority、setName 和 setUncaughtExceptionHandler 方法 | 此權限容許攻擊者修改系統中任意線程的行爲。 |
stopThread | 經過調用線程的 stop 方法中止線程 |
若是系統已授予代碼訪問該線程的權限,則此權限容許代碼中止系統中的任何線程。此權限會形成必定的危險,由於該代碼可能經過停止現有的線程來破壞系統。 |
modifyThreadGroup | 修改線程組,例如經過調用 ThreadGroup 的 destroy 、getParent 、resume 、setDaemon 、setMaxPriority 、stop 和 suspend 方法 |
此權限容許攻擊者建立線程組並設置它們的運行優先級。 |
getProtectionDomain | 獲取類的 ProtectionDomain | 此權限容許代碼得到特定代碼源的安全策略信息。雖然得到安全策略信息並不足以危及系統安全,但這確實會給攻擊者提供了可以更好地定位攻擊目標的其餘信息,例如本地文件名稱等。 |
getFileSystemAttributes | 獲取文件系統屬性 | 此權限容許代碼得到文件系統信息(如調用者可用的磁盤使用量或磁盤空間)。這存在潛在危險,由於它泄露了關於系統硬件配置的信息以及一些關於調用者寫入文件特權的信息。 |
readFileDescriptor | 讀取文件描述符 | 此權限容許代碼讀取與文件描述符讀取相關的特定文件。若是該文件包含機密數據,則此操做很是危險。 |
writeFileDescriptor | 寫入文件描述符 | 此權限容許代碼寫入與描述符相關的特定文件。此權限很危險,由於它可能容許惡意代碼傳播病毒,或者至少也會填滿整個磁盤。 |
loadLibrary.{庫名} | 動態連接指定的庫 | 容許 applet 具備加載本機代碼庫的權限是危險的,由於 Java 安全架構並未設計成能夠防止惡意行爲,而且也沒法在本機代碼的級別上防止惡意行爲。 |
accessClassInPackage.{包名} | 當類加載器調用 SecurityManager 的checkPackageAccess 方法時,經過類加載器的 loadClass 方法訪問指定的包 |
此權限容許代碼訪問它們一般沒法訪問的那些包中的類。惡意代碼可能利用這些類幫助它們實現破壞系統安全的企圖。 |
defineClassInPackage.{包名} | 當類加載器調用 SecurityManager 的 checkPackageDefinition 方法時,經過類加載器的 defineClass 方法定義指定的包中的類。 |
此權限容許代碼在特定包中定義類。這樣作很危險,由於具備此權限的惡意代碼可能在受信任的包中定義惡意類,好比 java.security 或 java.lang 。 |
accessDeclaredMembers | 訪問類的已聲明成員 | 此權限容許代碼查詢類的公共、受保護、默認(包)訪問和私有的字段和/或方法。儘管代碼能夠訪問私有和受保護字段和方法名稱,但它不能訪問私有/受保護字段數據而且不能調用任何私有方法。此外,惡意代碼可能使用該信息來更好地定位攻擊目標。並且,它能夠調用類中的任意公共方法和/或訪問公共字段。若是代碼不能用這些方法和字段將對象強制轉換爲類/接口,那麼它一般沒法調用這些方法和/或訪問該字段,而這可能很危險。 |
queuePrintJob | 打印做業請求的開始 | 這可能向打印機輸出敏感信息,或者只是浪費紙張。 |
getStackTrace | 獲取另外一個線程的堆棧追蹤信息。 | 此權限容許獲取另外一個線程的堆棧追蹤信息。此操做可能容許執行惡意代碼監視線程並發現應用程序中的弱點。 |
setDefaultUncaughtExceptionHandler | 在線程因爲未捕獲的異常而忽然終止時,設置將要使用的默認處理程序 | 此權限容許攻擊者註冊惡意的未捕獲異常處理程序,可能會妨礙線程的終止 |
Preferences | 表示獲得 java.util.prefs.Preferences 的訪問權所需的權限。java.util.prefs.Preferences 實現了用戶或系統的根,這反過來又容許獲取或更新 Preferences 持久內部存儲中的操做。 | 若是運行此代碼的用戶具備足夠的讀/寫內部存儲的 OS 特權,則此權限就容許用戶讀/寫優先級內部存儲。實際的內部存儲可能位於傳統的文件系統目錄中或註冊表中,這取決於平臺 OS。 |
當批量配置的時候,有三種模式:
能夠經過${}來引用系統屬性,如:
"file:${{java.ext.dirs}}/*"
當出現關於安全管理的報錯的時候,基本有兩種方式來解決。
通常狀況下都是無心啓動安全管理器,因此這時候只須要把安全管理器進行關閉,去掉啓動參數便可。
若由於沒有權限報錯,則報錯信息中會有請求的權限和請求什麼權限,以下:
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; };
這一篇簡單介紹Java的安全管理器,後面會對其進行詳細介紹,不過了解這些對通常應用已經足夠了。