如何在SecurityManager中检查调用者类的来源?

10

我有一个用于受信任应用程序代码的ClassLoader,还有一个用于用户提交的(不受信任)代码的ClassLoader。

我想让用户提交的代码受到Security Manager的限制。我如何在SecurityManager内部检查调用者的来源?请参见伪代码:

System.setSecurityManager(new SecurityManager() {
    public void checkPermission(Permission permission) {
        if (/*caller class is not loaded by the trusted classloader*/) {
            throw new SecurityException("You do not have permissions.");
        }
    }
});

我已经尝试过的方法:

  • StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE).getCallerClass().getClassLoader() 首先检查权限,因此会导致堆栈溢出异常。

  • Thread.currentThread().getStackTrace()[2].getClassLoaderName() 不安全,因为它只提供了类加载器名称,而没有提供类对象。如果不受信任的加载器的规范名称与受信任的加载器相同,则存在安全问题。


首先检查权限,以免引起堆栈溢出异常。是否可以通过特殊处理该权限检查以避免递归来解决这个问题? - the8472
@the8472,您能解释一下您想如何“特殊处理”它吗?因为您无法确定是哪个类请求使用StackWalker权限。它可能是SecurityManager,但也可能是恶意代码。 - Henk Schurink
好的,你只需尽早获取堆栈跟踪器,这样就不存在恶意代码,因此您可以在启动阶段对该特定调用进行确认而无需进一步检查。安全检查发生在getInstance()期间,因此您只需要授予一次即可。 - the8472
4
用户提交的代码应该在它自己的ProtectionDomain/CodeSource中,并且具有自己的权限(与你自己的代码分开)。这样,你就可以让SecurityManager自动处理它。 - Slaw
2个回答

5

首先,SecurityManager有一个受保护的方法getClassContext()
你的代码应该是这样的:

System.setSecurityManager(new SecurityManager() {
    public void checkPermission(Permission permission) {
        Class<?> caller = getClassContext()[1];
        ClassLoader ccl = caller.getClassLoader();
        if (ccl != null || ccl != getClass().getClassLoader()) {
            throw new SecurityException("You do not have permissions.");
        }
    }
});

其次,如果您想使用StackWalker,建议您重复使用StackWalker实例:

StackWalker walker = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
System.setSecurityManager(new SecurityManager() {
    public void checkPermission(Permission permission) {
        Class<?> caller = walker.getCallerClass();
        ClassLoader ccl = caller.getClassLoader();
        if (ccl != null || ccl != getClass().getClassLoader()) {
            throw new SecurityException("You do not have permissions.");
        }
    }
});

第三,这可能不会做你想要的事情。安全检查在JDK中随处可见,所以调用者可能距离您有任意多个堆栈级别,需要您检查整个堆栈(提示:在第二次访问堆栈中的SecurityManager时中断)。
取而代之的是,定义一个策略(创建一个java策略文件),在其中授予您的代码所有权限,并使用java.lang.SecurityManager。
如果不可能编写自己的策略文件,则还可以使用Policy.setPolicy()来安装自己实现的java.security.Policy。
一些实现java.security.Policy的提示:
  • 覆盖implies和两个getPermissions方法。真正重要。
  • 捕获您策略类的ProtectionDomain。(private static final ProtectionDomain MY_PD = MyPolicy.class.getProtectionDomain())
  • 如果检查是针对您自己的ProtectionDomain,请使用快速路径。在这种情况下不要调用其他代码,否则您可能会遇到StackOverflow。

0

我找到了一个临时解决方案,但它并不完美:

System.setSecurityManager(new SecurityManager() {
    public void checkPermission(Permission permission) {
        Class<?> caller = SecurityManager.class;
        Class<?>[] classContext = this.getClassContext();
        int loopAmount = 0;

        while (caller.getCanonicalName() == null
                 || !caller.getCanonicalName().startsWith("nl")) {
            caller = classContext[loopAmount];
            loopAmount++;
        }

        if (caller.getClassLoader() != trustedClassLoader) {
            throw new SecurityException("You do not have permissions.");
        }
    }
});

所有不受信任的类的规范名称必须以“nl”开头。据我所知,这是从类上下文中获取调用者类的唯一方法,因为调用者类的数组位置未知,上下文数组的最后一个元素始终是具有主方法的类。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接