我试图防止在主Java应用程序“内部”运行的插件访问它们不应访问的内容。我已经阅读了有关Policy,AccessControllers和ProtectionDomains的内容,但是它们非常面向JAR。
我已经试过了:
import java.nio.file.Files
import java.nio.file.Paths
import java.security.*
fun main(args: Array<String>) {
Policy.setPolicy(object : Policy() {})
System.setSecurityManager(SecurityManager())
val domain = ProtectionDomain(null, Permissions() /* no permissions */)
AccessController.doPrivileged(PrivilegedAction {
untrusted()
}, AccessControlContext(arrayOf(domain)))
}
fun untrusted() {
try {
// Works as expected
Files.readAllBytes(Paths.get("build.gradle"))
throw IllegalStateException("Was able to access file, but shouldn't have been able to")
} catch (e: AccessControlException) {
}
try {
// Should throw AccessControlException, but doesn't
AccessController.doPrivileged(PrivilegedAction {
Files.readAllBytes(Paths.get("build.gradle"))
})
throw IllegalStateException("Was able to access file, but shouldn't have been able to")
} catch (e: AccessControlException) {
}
}
即使我通过自定义的限制
untrusted()
调用ProtectionDomain
,似乎也可以轻易摆脱它。我期望doPrivileged
中的untrusted
调用可以与最外面的ProtectionDomain
(具有所有权限的主程序)和调用方的ProtectionDomain
(没有权限)的权限相交,导致untrusted
基本上具有0个权限。我也尝试过这样的域集:
val domain = ProtectionDomain(CodeSource(URL("http://foo"), null as Array<CodeSigner>?), Permissions() /* no permissions */)
但这也行不通-使用主程序的
Policy
而不是调用ProtectionDomain
的那个来查询untrusted()
。 (显然,我需要更新Policy
以正确处理“ http://foo”,但无论如何它甚至都不会检查ProtectionDomain
)那么我的理解哪里出错了?
最佳答案
在对此进行了一些研究之后,我想我有了答案。我可以写更长的答案,但我想我只是顺其自然。
由ClassLoader加载的每个类都有一个与之关联的ProtectionDomain + CodeSource。这些有点粗糙-CodeSource表示类的来源,但它不是指向单个.class
文件或任何内容的指针-而是指向目录或JAR。因此,同一JAR或目录中的两个类通常具有相同的权限。任何具有可识别ProtectionDomain + CodeSource的类或脚本都可以通过您的策略列入白名单/黑名单。
当然,例外情况(kinda)是带有许可参数的AccessController.doPrivileged。这使您可以限制代码区域的权限。但是从理论上讲,该代码可以仅使用回调来调用AccessController.doPrivileged
。该方法签名意味着“不要检查我的整个调用堆栈的权限;只需在策略文件中查找我的ProtectionDomain + CodeSource并查看其内容即可。”因此,如果您运行的是真正不受信任的代码,则最好确保做到这一点。它具有与您信任的应用程序不同的ProtectionDomain + CodeSource,并且b。您的策略能够识别该代码并授予其适当限制的权限。