java是一種類型安全的語言,它有四類稱爲安全沙箱機制的安全機制來保證語言的安全性,這四類安全沙箱分別是:java
類加載體系算法
.class文件檢驗器數據庫
安全管理器及Java API安全
本篇博客主要介紹「類安全管理器及Java API」的基本原理,如需瞭解其它幾類安全機制能夠經過上面的博客連接進入查看。app
java安全沙箱的前三類保證了jvm所運行程序的完整性,使得jvm不會由於運行有漏洞或惡意的代碼而致使出現不可預期的狀態。而第四類沙箱模型是「類安全管理器及Java API」,它能保護jvm在運行有漏洞或惡意的代碼不會破壞外部資源。java經過稱爲安全管理器的一類API來保證這類安全性。jvm
首先介紹下安全策略文件,若是啓用了安全管理器,默認會使用jre自帶的安全策略文件$JAVA_HOME/jre/lib/security/java.policy來指定訪問外部資源的權限,該策略文件也能夠經過jvm參數-Djava.security.policy來指定。ide
Policy文件的主要格式以下:測試
keystore "some_keystore_url", "keystore_type";
grant [SignedBy "signer_names"] [, CodeBase "URL"] { Permission permission_class_name ["target_name"] [,"action"] [,SignedBy"signer_names"]; … … };
"keystore"記錄 ui
一個keystore是一個私有密鑰(private keys)數據庫和相應的數字簽名,例如X.509證書。Policy文件中可能只有一條keystore記錄(也可能不含有該記錄),它能夠出如今文件中grant記錄之外的任何地方。Policy配置文件中指定的keystores用於尋找grant記錄中指定的、簽名者的公共密鑰(public keys),若是任何grant記錄指定簽名者(signer_names),那麼,keystore記錄必須出如今policy配置文件中。
"some_keystore_url"是指keystore的URL位置,"keystore_type"是指keystore的類型。第二個選項是可選項,若是沒有指定,該類型則假定由安全屬性文件(java.security)中的"keystore.type"屬性來肯定。keystore類型定義了keystore信息的存儲和數據格式,用於保護keystore中的私有密鑰和keystore完整性的算法。Sun Microsystems支持的缺省類型爲「JKS」。
"grant"記錄
在Policy文件中的每個grant記錄含有一個CodeSource(指定代碼)及其permission(許可)。Policy文件中的每一條grant記錄遵循下面的格式,以保留字「grant」開頭,表示一條新的記錄的開始,「Permission」是另外一個保留字,在記錄中用來標記一個新的許可的開始。每個grant記錄授予一個指定的代碼(CodeBase)一套許可(Permissions)。permission_class_name必須是一個合格並存在的全限定性類名,例如java.io.FilePermission。
target_name用來指定目標類的位置,action用於指定目標類擁有的權限。 target_name能夠直接指定類名(能夠是絕對或相對路徑),目錄名,也可使用通配符/、/*或着/-。
directory/ 表示directory目錄下的全部.class文件,不包括.jar文件
directory/* 表示directory目錄下的全部的.class及.jar文件
directory/- 表示directory目錄下的全部的.class及.jar文件,包括子目錄
下面是一個policy文件的demo:
grant codeBase "file:${{java.ext.dirs}}/*" { permission java.security.AllPermission; }; grant { permission java.lang.RuntimePermission "stopThread"; permission java.net.SocketPermission "localhost:1099", "listen"; permission java.util.PropertyPermission "java.version", "read"; ... ... };
例如:對於java.net.SocketPermission,action能夠是:listen,accept,connect,read,write;對於java.io.FilePermission,action能夠是:read, write, delete和execute。
安全管理器
java的安全管理器能夠定製,也可使用jdk的默認實現java.lang.SecurityManager,啓動安全管理器的話有兩種方式,一種是經過硬編碼的方式啓動,另一種是經過jvm參數-Djava.security.manager啓動。
下面的測試用例都使用jre的默認policy文件配置:
grant { ... ... permission java.util.PropertyPermission "java.version", "read"; ... ... };
該策略文件指定了"java.version"的讀權限,而後並無指定寫權限。參考如下測試用例:
public static void main(String... args) { String javaVersion=System.getProperty("java.version"); System.err.println(javaVersion); System.setProperty("java.version","1.7.0_45"); String javaNewVersion=System.getProperty("java.version"); System.err.println(javaNewVersion); }
首先讀"java.version"屬性,而後把該屬性改寫爲1.7.0_45,最後再讀取它並打印出來,輸出結果爲:
1.8.0_45 1.7.0_45
能夠看到默認的jdk版本爲1.8.0_45(1.8.0是java的主版本號,45是次版本號)。
而後前面的policy文件只指定了read權限,爲何這裏卻write成功了?那是由於默認狀況下java並不啓動安全管理器,可使用硬編碼System.setSecurityManager()來啓動安全管理器,參考如下測試用例:
public static void main(String... args) { // 啓用安全管理器 System.setSecurityManager(new SecurityManager()); String javaVersion=System.getProperty("java.version"); System.err.println(javaVersion); System.setProperty("java.version","1.7.0_45"); String javaNewVersion=System.getProperty("java.version"); System.err.println(javaNewVersion); }
此時的輸出結果爲:
1.8.0_45 Exception in thread "main" java.security.AccessControlException: access denied ("java.util.PropertyPermission" "java.version" "write") at java.security.AccessControlContext.checkPermission(AccessControlContext.java:457) at java.security.AccessController.checkPermission(AccessController.java:884) at java.lang.SecurityManager.checkPermission(SecurityManager.java:549) at java.lang.System.setProperty(System.java:792) at test.Test.main(Test.java:9)
結果很明確:能夠讀,但不能寫,咱們能夠來修改policy文件,讓它支持"java.version"的寫操做:
grant { ... ... permission java.util.PropertyPermission "java.version", "read"; permission java.util.PropertyPermission "java.version", "write"; ... ... };
此時再執行上面的用例,輸出結果爲:
1.8.0_45 1.7.0_45
能夠看到此時能夠支持"java.version"的寫操做了。
另外使用jvm參數-Djava.security.manager也能啓用安全管理器,此時jvm啓動時會設置"java.security.manager"系統屬性爲空字符串"",此時會在啓動sun.misc.Launcher時初始化安全管理器,查看sun.misc.Launcher的源碼:
public Launcher(){ ExtClassLoader extclassloader; try { extclassloader = ExtClassLoader.getExtClassLoader(); } catch(IOException ioexception) { throw new InternalError("Could not create extension class loader", ioexception); } try { loader = AppClassLoader.getAppClassLoader(extclassloader); } catch(IOException ioexception1) { throw new InternalError("Could not create application class loader", ioexception1); } Thread.currentThread().setContextClassLoader(loader); String s = System.getProperty("java.security.manager"); if(s != null) { SecurityManager securitymanager = null; if("".equals(s) || "default".equals(s)) securitymanager = new SecurityManager(); else try { securitymanager = (SecurityManager)loader.loadClass(s).newInstance(); } catch(IllegalAccessException illegalaccessexception) { } catch(InstantiationException instantiationexception) { } catch(ClassNotFoundException classnotfoundexception) { } catch(ClassCastException classcastexception) { } if(securitymanager != null) System.setSecurityManager(securitymanager); else throw new InternalError((new StringBuilder()).append("Could not create SecurityManager: ").append(s).toString()); } }
能夠看到"java.security.manager"系統屬性爲空字符串""時會啓用jdk的默認安全管理器SecurityManager。
java的安全機制api大部分都在java.security包下,由於源碼不少,就不貼出來了,你們感興趣的話能夠研究下。如下是一些經常使用的類的api介紹:
java.security.AccessControlContext:基於它所封裝的上下文做出系統資源訪問決定,該類最經常使用於將代碼標記爲享有「特權」。
java.security.AccessController:用於與訪問控制相關的操做和決定。java.security.SecureClassLoader此類擴展了 ClassLoader,支持使用相關的代碼源和權限定義類,這些代碼源和權限默認狀況下可根據系統策略獲取到。
java.security.Provider:此類表示 Java 安全 API "provider",這裏 provider 實現了 Java 安全性的一部分或者所有。
java.security.Permission:表示訪問系統資源的抽象類。全部權限都有一個名稱(對它們的解釋依賴於子類),以及用來定義特定 Permission 子類的語義的抽象方法。