1. 程式人生 > >Java中4種安全沙箱機制之安全管理器及Java API

Java中4種安全沙箱機制之安全管理器及Java API

簡介

java安全沙箱的前三類保證了jvm所執行程式的完整性,使得jvm不會因為執行有漏洞或惡意的程式碼而導致出現不可預期的狀態。而第四類沙箱模型是“類安全管理器及Java API”,它能保護jvm在執行有漏洞或惡意的程式碼不會破壞外部資源。java通過稱為安全管理器的一類API來保證這類安全性。

安全策略檔案

首先介紹下安全策略檔案,如果啟用了安全管理器,預設會使用jre自帶的安全策略檔案$JAVA_HOME/jre/lib/security/java.policy來指定訪問外部資源的許可權,該策略檔案也可以通過jvm引數-Djava.security.policy來指定。

Policy檔案的主要格式如下:

keystore "some_keystore_url""keystore_type";
grant [SignedBy "signer_names"] [, CodeBase "URL"] {
    Permission permission_class_name ["target_name"] [,"action"] [,SignedBy"signer_names"];
    … …
};
  1. "keystore"記錄 

    一個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”。

  2. "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.SocketPermissionaction可以是:listenacceptconnectreadwrite;對於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 "mainjava.security.AccessControlExceptionaccess 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的安全機制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 子類的語義的抽象方法。