java中的安全模型(沙箱機制)

本博文轉載自java中的安全模型(沙箱機制)html

本博文整合自:Java安全——理解Java沙箱Java 安全模型介紹Java的沙箱機制原理入門java

相關介紹:

 咱們都知道,程序員編寫一個Java程序,默認的狀況下能夠訪問該機器的任意資源,好比讀取,刪除一些文件或者網絡操做等。當你把程序部署到正式的服務器上,系統管理員要爲服務器的安全承擔責任,那麼他可能不敢肯定你的程序會不會訪問不應訪問的資源,爲了消除潛在的安全隱患,他可能有兩種辦法:git

  • 讓你的程序在一個限定權限的賬號下運行。
  • 利用Java的沙箱機制來限定你的程序不能爲非做歹。如下用於介紹該機制。
  • 什麼是沙箱?

     Java安全模型的核心就是Java沙箱(sandbox),什麼是沙箱?沙箱是一個限制程序運行的環境。沙箱機制就是將 Java 代碼限定在虛擬機(JVM)特定的運行範圍中,而且嚴格限制代碼對本地系統資源訪問,經過這樣的措施來保證對代碼的有效隔離,防止對本地系統形成破壞。沙箱主要限制系統資源訪問,那系統資源包括什麼?——CPU、內存、文件系統、網絡。不一樣級別的沙箱對這些資源訪問的限制也能夠不同。程序員

     全部的Java程序運行均可以指定沙箱,能夠定製安全策略。github

    java中的安全模型:

     在Java中將執行程序分紅本地代碼和遠程代碼兩種,本地代碼默認視爲可信任的,而遠程代碼則被看做是不受信的。對於授信的本地代碼,能夠訪問一切本地資源。而對於非授信的遠程代碼在早期的Java實現中,安全依賴於沙箱 (Sandbox) 機制。以下圖所示安全

    JDK1.0安全模型

    但如此嚴格的安全機制也給程序的功能擴展帶來障礙,好比當用戶但願遠程代碼訪問本地系統的文件時候,就沒法實現。所以在後續的 Java1.1 版本中,針對安全機制作了改進,增長了安全策略,容許用戶指定代碼對本地資源的訪問權限。以下圖所示服務器

    JDK1.1安全模型

    在 Java1.2 版本中,再次改進了安全機制,增長了代碼簽名。不論本地代碼或是遠程代碼,都會按照用戶的安全策略設定,由類加載器加載到虛擬機中權限不一樣的運行空間,來實現差別化的代碼執行權限控制。以下圖所示markdown

    JDK1.2安全模型

    當前最新的安全機制實現,則引入了域 (Domain) 的概念。虛擬機會把全部代碼加載到不一樣的系統域和應用域,系統域部分專門負責與關鍵資源進行交互,而各個應用域部分則經過系統域的部分代理來對各類須要的資源進行訪問。虛擬機中不一樣的受保護域 (Protected Domain),對應不同的權限 (Permission)。存在於不一樣域中的類文件就具備了當前域的所有權限,以下圖所示網絡

    最新的安全模型

    以上提到的都是基本的 Java 安全模型概念,在應用開發中還有一些關於安全的複雜用法,其中最經常使用到的 API 就是 doPrivileged。doPrivileged 方法可以使一段受信任代碼得到更大的權限,甚至比調用它的應用程序還要多,可作到臨時訪問更多的資源。有時候這是很是必要的,能夠應付一些特殊的應用場景。例如,應用程序可能沒法直接訪問某些系統資源,但這樣的應用程序必須獲得這些資源纔可以完成功能。dom

    組成沙箱的基本組件:

    • 字節碼校驗器(bytecode verifier):確保Java類文件遵循Java語言規範。這樣能夠幫助Java程序實現內存保護。但並非全部的類文件都會通過字節碼校驗,好比核心類。

    • 類裝載器(class loader):其中類裝載器在3個方面對Java沙箱起做用
    1. 它防止惡意代碼去幹涉善意的代碼;
    2. 它守護了被信任的類庫邊界;
    3. 它將代碼納入保護域,肯定了代碼能夠進行哪些操做。

     虛擬機爲不一樣的類加載器載入的類提供不一樣的命名空間,命名空間由一系列惟一的名稱組成,每個被裝載的類將有一個名字,這個命名空間是由Java虛擬機爲每個類裝載器維護的,它們互相之間甚至不可見。

     類裝載器採用的機制是雙親委派模式。1. 從最內層JVM自帶類加載器開始加載,外層惡意同名類得不到加載從而沒法使用;

  • 因爲嚴格經過包來區分了訪問域,外層惡意的類經過內置代碼也沒法得到權限訪問到內層類,破壞代碼就天然沒法生效。
    • 存取控制器(access controller):存取控制器能夠控制核心API對操做系統的存取權限,而這個控制的策略設定,能夠由用戶指定。

    • 安全管理器(security manager):是核心API和操做系統之間的主要接口。實現權限控制,比存取控制器優先級高。

    • 安全軟件包(security package):java.security下的類和擴展包下的類,容許用戶爲本身的應用增長新的安全特性,包括:

  • 安全提供者
  • 消息摘要
  • 數字簽名
  • 加密
  • 鑑別
  • 沙箱包含的要素:

    1. 權限

     權限是指容許代碼執行的操做。包含三部分:權限類型、權限名和容許的操做。權限類型是實現了權限的Java類名,是必需的。權限名通常就是對哪類資源進行操做的資源定位(好比一個文件名或者通配符、網絡主機等),通常基於權限類型來設置,有的好比java.security.AllPermission不須要權限名。容許的操做也和權限類型對應,指定了對目標能夠執行的操做行爲,好比讀、寫等。以下面的例子:

    permission java.security.AllPermission;    //權限類型
    permission java.lang.RuntimePermission "stopThread";    //權限類型+權限名
    permission java.io.FilePermission "/tmp/foo" "read";    //權限類型+權限名+容許的操做
    標準權限:
    說明 類型 權限名 操做 例子
    文件權限 java.io.FilePermission 文件名(平臺依賴) 讀、寫、刪除、執行 容許全部問價的讀寫刪除執行:permission java.io.FilePermission "<< ALL FILES>>", "read,write,delete,execute";。容許對用戶主目錄的讀:permission java.io.FilePermission "${user.home}/-", "read";
    套接字權限 java.net.SocketPermission 主機名:端口 接收、監聽、鏈接、解析 容許實現全部套接字操做:permission java.net.SocketPermission ":1-", "accept,listen,connect,resolve";。容許創建到特定網站的鏈接:permission java.net.SocketPermission ".abc.com:1-", "connect,resolve";
    屬性權限 java.util.PropertyPermission 須要訪問的jvm屬性名 讀、寫 讀標準Java屬性:permission java.util.PropertyPermission "java.", "read";。在sdo包中建立屬性:permission java.util.PropertyPermission "sdo.", "read,write";
    運行時權限 java.lang.RuntimePermission 多種權限名[見附錄A] 容許代碼初始化打印任務:permission java.lang.RuntimePermission "queuePrintJob"
    AWT權限 java.awt.AWTPermission 6種權限名[見附錄B] 容許代碼充分使用robot類:permission java.awt.AWTPermission "createRobot"; permission java.awt.AWTPermission "readDisplayPixels";
    網絡權限 java.net.NetPermission 3種權限名[見附錄C] 容許安裝流處理器:permission java.net.NetPermission "specifyStreamHandler";。
    安全權限 java.security.SecurityPermission 多種權限名[見附錄D]
    序列化權限 java.io.SerializablePermission 2種權限名[見附錄E]
    反射權限 java.lang.reflect.ReflectPermission suppressAccessChecks(容許利用反射檢查任意類的私有變量)
    徹底權限 java.security.AllPermission 無(擁有執行任何操做的權限)

    2. 代碼源

     代碼源是類所在的位置,表示爲以URL地址。

    3. 保護域

     保護域用來組合代碼源和權限,這是沙箱的基本概念。保護域就在於聲明瞭好比由代碼A能夠作權限B這樣的事情。

    4. 策略文件

     策略文件是控制沙箱的管理要素,一個策略文件包含一個或多個保護域的項。策略文件完成了代碼權限的指定任務,策略文件包括全局和用戶專屬兩種。

     爲了管理沙箱,策略文件我認爲是最重要的內容。JVM可使用多個策略文件,不過通常兩個最經常使用。一個是全局的:$JREHOME/lib/security/java.policy,做用於JVM的全部實例。另外一個是用戶本身的,能夠存儲到用戶的主目錄下。策略文件可使用jdk自帶的policytool工具編輯。

    默認的策略文件咱們先參考一下:

    // 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";
            // permission for standard RMI registry port
            permission java.net.SocketPermission "localhost:1099", "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";
    };

     策略文件的內容格式就是這樣,grant受權容許操做某個權限。這個默認的策略文件就指明瞭jdk擴展包能夠有所有權限,容許代碼stop線程,容許監聽1099端口(1099號端口,是默認的服務器端RMI監聽端口)等等。

     另外一個很重要的是參數文件——java.security,這個文件和策略文件在同一個目錄下。這個參數文件定義了沙箱的一些參數。好比默認的沙箱文件是這樣的(截取部分):

    # The default is to have a single system-wide policy file,
    # and a policy file in the user's home directory.
    policy.url.1=file:${java.home}/lib/security/java.policy
    policy.url.2=file:${user.home}/.java.policy
    # whether or not we expand properties in the policy file
    # if this is set to false, properties (${...}) will not be expanded in policy
    # files.
    policy.expandProperties=true
    # whether or not we allow an extra policy to be passed on the command line
    # with -Djava.security.policy=somefile. Comment out this line to disable
    # this feature.
    policy.allowSystemProperty=true

     policy.url.*這個屬性指明瞭使用的策略文件,如上文所述,默認的兩個位置就在這裏配置,用戶能夠自行更改順序和存儲位置。而policy.allowSystemProperty指明是否容許用戶自行經過命令行指定policy文件。

    5. 密鑰庫

     保存密鑰證書的地方。

    默認沙箱

     經過Java命令行啓動的Java應用程序,默認不啓用沙箱。要想啓用沙箱,啓動命令須要作以下形式的變動:

    java -Djava.security.manager <other args>

    沙箱啓動後,安全管理器會使用兩個默認的策略文件來肯定沙箱啓動參數。固然也能夠經過命令指定:

    java -Djava.security.policy=<URL>

    若是要求啓動時只遵循一個策略文件,那麼啓動參數要加個等號,以下:

    java -Djava.security.policy==<URL>

    如何使用

    1. 限制讀文件

    這個例子很簡單,首先寫一個r.txt文件,裏面的內容是「abcd」,再寫個程序以下讀取這個r.txt。

    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    import java.io.InputStream;
    public class PolicyTest {
        public static void file() {
            File f = new File("D:\\github\\CDLib\\src\\main\\resources\\security\\r.txt");
            InputStream is;
            try {
                is = new FileInputStream(f);
                byte[] content = new byte[1024];
                while (is.read(content) != -1) {
                    System.out.println(new String(content));
                }
            } catch (FileNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        public static void main(String[] args) {
            // test read file.
            file();
        }
    }

    發現輸出是abcd。

     接下來修改java啓動參數,加入-Djava.security.manager,啓動了安全沙箱。再運行,輸出變成了異常

    Exception in thread "main" java.security.AccessControlException: access denied ("java.io.FilePermission" "D:\github\CDLib\src\main\resources\security\r.txt" "read") 
    at java.security.AccessControlContext.checkPermission(Unknown Source) 
    at java.security.AccessController.checkPermission(Unknown Source) 
    at java.lang.SecurityManager.checkPermission(Unknown Source) 
    at java.lang.SecurityManager.checkRead(Unknown Source) 
    at java.io.FileInputStream.(Unknown Source) 
    at com.taobao.cd.security.PolicyTest.main(PolicyTest.java:15)

    這裏已經提示了,訪問被拒絕,說明了沙箱啓動,同時也驗證了默認沙箱——禁止本地文件訪問。

    再來,咱們構建一個custom.policy文件以下:

    grant {
        permission java.io.FilePermission "D:\\github\\CDLib\\src\\main\\resources\\security\\*", "read";
    };

    這裏構建了一條安全策略——容許讀取security目錄下的文件。

    修改啓動命令,添加

    -Djava.security.policy=D:\\github\\CDLib\\src\\main\\resources\\security\\custom.policy

    再執行,結果輸出了abcd。

    如上例。咱們經過自定義policy文件修改了默認沙箱的安全策略,再經過啓動參數開啓沙箱模式。這樣就能夠構造咱們本身想要的沙箱效果了。

    附錄

    A

    權限名 用途說明
    accessClassInPackage. 容許代碼訪問指定包中的類
    accessDeclaredMembers 容許代碼使用反射訪問其餘類中私有或保護的成員
    createClassLoader 容許代碼實例化類加載器
    createSecurityManager 容許代碼實例化安全管理器,它將容許程序化的實現對沙箱的控制
    defineClassInPackage. 容許代碼在指定包中定義類
    exitVM 容許代碼關閉整個虛擬機
    getClassLoader 容許代碼訪問類加載器以得到某個特定的類
    getProtectionDomain 容許代碼訪問保護域對象以得到某個特定類
    loadlibrary. 容許代碼裝載指定類庫
    modifyThread 容許代碼調整指定的線程參數
    modifyThreadGroup 容許代碼調整指定的線程組參數
    queuePrintJob 容許代碼初始化一個打印任務
    readFileDescriptor 容許代碼讀文件描述符(相應的文件是由其餘保護域中的代碼打開的)
    setContextClassLoader 容許代碼爲某線程設置上下文類加載器
    setFactory 容許代碼建立套接字工廠
    setIO 容許代碼重定向System.in、System.out或System.err輸入輸出流
    setSecurityManager 容許代碼設置安全管理器
    stopThread 容許代碼調用線程類的stop()方法
    writeFileDescriptor 容許代碼寫文件描述符

    B

    權限名 用途說明
    accessClipboard 容許訪問系統的全局剪貼板
    accessEventQueue 容許直接訪問事件隊列
    createRobot 容許代碼建立AWT的Robot類
    listenToAllAWTEvents 容許代碼直接監聽事件分發
    readDisplayPixels 容許AWT Robot讀顯示屏上的像素
    showWindowWithoutWarningBanner 容許建立無標題欄的窗口

    C

    權限名 用途說明
    specifyStreamHandler 容許在URL類中安裝新的流處理器
    setDefaultAuthenticator 能夠安裝鑑別類
    requestPassworkAuthentication 能夠完成鑑別

    D

    權限名 用途說明
    addIdentityCertificate 爲Identity增長一個證書
    clearProviderProperties. 針對指定的提供者,刪除全部屬性
    createAccessControlContext 容許建立一個存取控制器的上下文環境
    getDomainCombiner 容許撤銷保護域
    getPolicy 檢索能夠實現沙箱策略的類
    getProperty. 讀取指定的安全屬性
    getSignerPrivateKey 由Signer對象獲取私有密鑰
    insertProvider. 將指定的提供者添加到響應的安全提供者組中
    loadProviderProperties. 裝載指定的提供者的屬性
    printIdentity 打印Identity類內容
    putAllProviderProperties. 更新指定的提供者的屬性
    putProviderProperty. 爲指定的提供者增長一個屬性
    removeIdentityCertificate 取消Identity對象的證書
    removeProvider. 將指定的提供者從相應的安全提供者組中刪除
    removeProviderProperty. 刪除指定的安全提供者的某個屬性
    setIdentityInfo 爲某個Identity對象設置信息串
    setIdentityPublicKey 爲某個Identity對象設置公鑰
    setPolicy 設置能夠實現沙箱策略的類
    setProperty. 設置指定的安全屬性
    setSignerKeyPair 在Signer對象中設置密鑰對
    setSystemScope 設置系統所用的IdentityScope

    E

    權限名 用途說明
    enableSubstitution 容許實現ObjectInputStream類的enableResolveObject()方法和ObjectOutputStream類的enableReplaceObject()方法
    enableSubclassImplementation 容許ObjectInputStream和ObjectOutputStream建立子類,子類能夠覆蓋readObject()和writeObject()方法
相關文章
相關標籤/搜索