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 配置基本原則
在啟用安全管理器的時候,配置遵循以下基本原則:
- 沒有配置的許可權表示沒有。
- 只能配置有什麼許可權,不能配置禁止做什麼。
- 同一種許可權可多次配置,取並集。
- 統一資源的多種許可權可用逗號分割。
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 套接字工廠的實際實現。攻擊者可能設定錯誤的實現,從而破壞資料流。 |
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。 |
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; };
評論列表