天天看點

【Java 安全技術探索之路系列:J2SE安全架構】之二:安全管理器一 安全管理器的功能一 安全管理器的使用三 實作自己定義的安全管理器

作者:郭嘉

郵箱:[email protected]

部落格:http://blog.csdn.net/allenwells

github:https://github.com/AllenWell

一 安全管理器的功能

安全管理器是一個同意程式實作安全政策的類,它會在執行階段檢查須要保護的資源的訪問權限及其它規定的操作權限。保護系統免受惡意操作攻擊。以達到系統的安全政策。

安全管理器負責檢查的操作主要包括以下幾個:

  • 建立一個新的類載入器
  • 退出虛拟機
  • 使用反射訪問還有一個類的成員
  • 訪問本地連接配接
  • 打開socket連接配接
  • 啟動列印作業
  • 訪問系統剪貼闆
  • 訪問AWT事件隊列
  • 打開一個頂層窗體

注意:在執行Java應用程式時,預設的設定是不安裝安全管理器的,這樣全部的操作都是同意的,

安全管理器的工作流程例如以下圖所看到的:

【Java 安全技術探索之路系列:J2SE安全架構】之二:安全管理器一 安全管理器的功能一 安全管理器的使用三 實作自己定義的安全管理器

一 安全管理器的使用

1.1 擷取安全管理器

Security security = System.getSecurityManager();
           

1.2 啟動安全管理器

1.2.1 指令行啟動

java -Djava.security.manager class_name
           

1.2.2 程式啟動

在啟動安全管理器時能夠通過-Djava.security.policy選項來指定安全政策檔案。

假設沒有指定政策檔案的路徑,那麼安全管理器将使用預設的安全政策檔案,它位于%JAVA_HOME%/jre/lib/security檔案夾以下的java.policy。

注意:

  • =表示這個政策檔案将和預設的政策檔案一同發揮作用。
  • ==表示僅僅使用這個政策檔案。

policy檔案包括了多個grant語句,每個grant描寫叙述某些代碼擁有某些操作的權限。在啟動安全管理器時會依據policy檔案生成一個Policy對象,不論什麼時候一個應用程式僅僅能有一個Policy對象。

SecurityManager sm=new SecurityManager();
System.setSecurityManager(sm);
           

預設的%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 dynamic ports
        permission java.net.SocketPermission "localhost:0", "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";
};
           

1.3 關閉安全管理器

SecurityManager sm=System.getSecurityManager();
if(sm!=null)
{
    System.setSecurityManager(null);
}
           

以上代碼僅僅有在位于{JDK_HOME}/jre/lib/security檔案夾下或者其它指定檔案夾下的java.policy檔案裡指定了一個權限才會生效。

該權限是:

1.4 安全管理器檢查

security.checkXXX(...);
           

檢查完畢後,成功則安全管理器傳回。失敗則安全管理器抛出SecurityException,注意該約定唯一的例外是checkTopLevelWindow。它傳回boolean值。

1.5 安全管理器權限檢查

安全管理器中全部其它check()方法的預設實作都是調用SecurityManager.checkPermission()方法來确定線程是否具有執行所請求的操作的權限。

僅僅帶有單個權限參數的checkPermission()方法總是在目前執行的線程上下文中執行安全檢查。

假設在給定的上下文進行檢查須要在不同的上下文中進行,能夠使用Java提供的包括上下文參數的getSecurityContext()方法和checkPermission()方法,例如以下所看到的:

Object context = null;
SecurityManager sm = System.getSecurityManager();
if(sm != null)
{
    context = sm.getSecurityContext();//該方法傳回目前調用上下文的一個快照
    sm.checkPermission(permission, context);//該方法使用一個上下文對象,以及依據該上下文(不是目前執行線程的上下文)作出訪問決策的權限。
}
           

權限分為以下幾個類别:

  • 檔案
  • 套接字
  • 網絡
  • 安全性
  • 執行時
  • 屬性
  • AWT
  • 反射
  • 可序列化

相應的權限類為:

  • java.io.FilePermission
  • java.net.SocketPermission
  • java.net.NetPermission
  • java.security.SecurityPermission
  • java.lang.RuntimePermission
  • java.util.PropertyPermission
  • java.awt.AWTPermission
  • java.lang.reflect
  • ReflectPermission
  • java.io.SerializablePermission

整個權限類的層次結構例如以下圖所看到的:

【Java 安全技術探索之路系列:J2SE安全架構】之二:安全管理器一 安全管理器的功能一 安全管理器的使用三 實作自己定義的安全管理器

以下寫一個樣例來示範一下自己定義安全管理器的使用。

import java.io.FileInputStream;
import java.io.FileNotFoundException;
public class SecurityManagerDemo
{
     public static void main(String[] args) throws FileNotFoundException 
    {
         System.out.println("SecurityManager: " + System.getSecurityManager());
         FileInputStream fis = new FileInputStream("C:\\Users\\Administrator\\my.txt");
         System.out.println(System.getProperty("file.encoding"));
    }
}
           

注意:my.txt是已經存在的檔案,須要在你的檔案夾建立,這裡的檔案夾是C:\Users\Administrator。

直接執行

直接執行SecurityManagerDemo,相當于沒有啟動安全管理器,SecurityManager列印出來為null,且能正确讀取protect.txt檔案跟file.encoding屬性。例如以下圖所看到的:

【Java 安全技術探索之路系列:J2SE安全架構】之二:安全管理器一 安全管理器的功能一 安全管理器的使用三 實作自己定義的安全管理器

加入啟動參數執行

加入啟動參數

-Djava.security.manager -Djava.security.policy=C:\\Users\\Administrator\\my.policy//自己定義政策檔案
           

指定-Djava.security.manager參數,此時SecurityManager列印出來為不為null,my.policy裡面并沒有做不論什麼授權。是以在讀取檔案的時就抛出AccessControlException異常,例如以下圖所看到的:

【Java 安全技術探索之路系列:J2SE安全架構】之二:安全管理器一 安全管理器的功能一 安全管理器的使用三 實作自己定義的安全管理器

建立my.policy,并寫入以下grant:

grant {
permission java.io.FilePermission "C:\\Users\\Administrator\\my.txt", "read";
permission java.util.PropertyPermission "file.encoding", "read";
};
           

此時能夠正确讀取。例如以下圖所看到的:

【Java 安全技術探索之路系列:J2SE安全架構】之二:安全管理器一 安全管理器的功能一 安全管理器的使用三 實作自己定義的安全管理器

三 實作自己定義的安全管理器

實作自己定義的安全管理器一般分為兩步:

  1. 建立一個SecurityManager子類,依據須要重寫一些方法。
  2. 依據應用程式代碼的權限須要配置政策檔案。

以下寫一個樣例來示範一下自己定義安全管理器的使用:

自己定義類MySecurityManager繼承于SecurityManager,重寫了checkRead()方法。

public class MySecurityManager extends SecurityManager 
{  

    @Override  
    public void checkRead(String file) 
    {  
        //super.checkRead(file, context);  
        if (file.endsWith("not")) 
        {
            throw new SecurityException("你沒有讀取的本檔案的權限");    
        }           
    }  

}  
           

寫個測試類MySecurityManagerDemo觀察MySecurityManager是否實用。

import java.io.FileInputStream;  
import java.io.IOException;  

public class MySecurityManagerDemo 
{  
    public static void main(String[] args)
    {  
        System.setSecurityManager(new MySecurityManager());  
        try 
        {  
            FileInputStream fis = new FileInputStream("not");  
            System.out.println(fis.read());  
        } 
        catch (IOException e) 
        {  
            e.printStackTrace();  
        }  
      }  
}  
           

執行完畢後,輸出列印“你沒有讀取的本檔案的權限”。說明MySecurityManager能夠使用。結果例如以下圖所看到的:

【Java 安全技術探索之路系列:J2SE安全架構】之二:安全管理器一 安全管理器的功能一 安全管理器的使用三 實作自己定義的安全管理器