在上一篇文章中,我們研究了如何保護移動Java代碼 。 這樣做的一種選擇是在籠子或沙箱中運行代碼。
這篇文章探討了如何為Java應用程序設置這樣的沙箱。
安全經理
Java中支持沙箱的安全性設施是java.lang.SecurityManager
。
默認情況下,Java在沒有SecurityManager
情況下運行,因此您應在應用程序中添加代碼以啟用以下代碼:
System.setSecurityManager(new SecurityManager());
您可以使用標準的SecurityManager
或后代。
SecurityManager
有許多checkXXX()
方法,這些方法都轉發給checkPermission(permission, context)
。 此方法要求AccessController
進行實際工作(請參見下文)。
[ checkXXX()
方法是Java 1.1的遺物 。]
如果允許請求的訪問,則checkPermission()
安靜地返回。 如果被拒絕,則拋出java.lang.SecurityException
。
實現沙箱的代碼應在執行敏感操作之前調用checkXXX
方法:
SecurityManager securityManager = System.getSecurityManager();
if (securityManager != null) {Permission permission = ...;securityManager.checkPermission(permission);
}
JRE包含許多地方的代碼。
權限
權限表示對系統資源的訪問。
為了允許這種訪問,必須向嘗試訪問的代碼顯式授予相應的許可(請參見下文)。
權限源自java.security.Permission 。 它們有一個名稱和一個可選的操作列表(以逗號分隔的字符串值形式)。
Java附帶了許多預定義的權限,例如FilePermission 。 您還可以添加自己的權限 。
以下是read
文件/home/remon/thesis.pdf
的權限:
Permission readPermission = new java.io.FilePermission('/home/remon/thesis.pdf', 'read');
您可以通過授予代碼權限AllPermission
來執行任何事情。 這與不使用SecurityManager
運行它具有相同的效果。
政策規定
使用策略 授予權限。 策略負責確定代碼是否有權執行安全敏感的操作。
AccessController
查閱Policy
以查看是否授予了Permission
。
在任何給定時間只能使用一個Policy
對象。 應用程序代碼可以將Policy
子類化以提供自定義實現 。
Policy
的默認實現使用配置文件加載授權。 有一個系統范圍的策略文件和一個(可選)用戶策略文件 。
您可以使用PolicyTool
程序創建其他策略配置文件。 每個配置文件都必須使用UTF-8編碼。
默認情況下,根本不授予代碼任何權限。 每個grant
語句都會添加一些權限。 授予的權限無法撤消。
以下策略片斷授予代碼,從起源/home/remon/code/
目錄read
權限的文件/home/remon/thesis.pdf
:
grant codeBase 'file:/home/remon/code/-' {permission java.io.FilePermission '/home/remon/thesis.pdf','read';
};
請注意, codeBase
的部分是URL ,因此,即使在Windows系統上,也應始終使用正斜杠。
帶尾隨/
codeBase
匹配指定目錄中的所有類文件(不是JAR文件)。 帶有尾隨/*
codeBase
匹配該目錄中包含的所有文件(類和JAR文件)。 帶有尾隨/-
codeBase
匹配目錄中的所有文件(類和JAR文件),并遞歸匹配該目錄中包含的子目錄中的所有文件。
對于Windows系統上文件權限中的路徑,您需要使用雙反斜杠( \\
),因為\
是轉義字符:
grant codeBase 'file:/C:/Users/remon/code/-' {permission java.io.FilePermission'C:\\Users\\remon\\thesis.pdf', 'read';
};
為了獲得更大的靈活性,您可以編寫帶有可變部分的贈款。 我們已經看到了codeBase
通配符。 您也可以替換系統屬性 :
grant codeBase 'file:/${user.home}/code/-' {permission java.io.FilePermission'${user.home}${/}thesis.pdf', 'read';
};
注意
${/}
替換為系統的路徑分隔符。 無需在 codeBase
,因為這是一個URL。
簽名碼
當然,我們應該確保我們使用的代碼是簽名的 ,以便我們知道它實際上是我們認為來自的人。
我們可以使用signedBy
子句在策略中測試簽名:
keystore 'my.keystore';
grant signedBy 'signer.alias', codeBase ... {...
};
這一政策片段使用的密鑰庫別名為my.keystore
來查找公鑰證書與別名signer.alias
。
然后,它驗證執行代碼已由與找到的證書中的公鑰相對應的私鑰簽名。
只能有一個keystore
條目。
codeBase
和signedBy
子句的組合指定了ProtectionDomain
。 同一ProtectionDomain
中的所有類都具有相同的權限。
特權代碼
每當嘗試進行資源訪問時,堆棧上的所有代碼都必須具有該資源訪問的權限,除非堆棧上的某些代碼已標記為privileged 。
將代碼標記為特權可使一段受信任的代碼臨時允許訪問比直接調用它的代碼更多的資源。 換句話說,安全系統將所有呼叫者視為來自發出特權呼叫的類的ProtectionDomain
的所有呼叫者,但僅在特權呼叫的持續時間內。
您可以通過在AccessController.doPrivileged()
調用中運行代碼來使代碼具有特權:
AccessController.doPrivileged(new PrivilegedAction() {public Object run() {// ...privileged code goes here...return null; }
});
組裝沙箱
現在我們有了組裝沙箱所需的所有零件:
- 安裝
SecurityManager
- 簽名應用程序罐
- 授予我們簽名的所有代碼
AllPermission
- 在移動代碼可能調用的地方添加權限檢查
- 權限檢查
doPrivileged()
塊后運行代碼
我在GitHub上創建了一個簡單的示例。
參考: 安全軟件開發博客中來自我們JCG合作伙伴 Remon Sinnema的Java代碼沙盒 。
翻譯自: https://www.javacodegeeks.com/2012/11/sandboxing-java-code.html