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

How to check caller class origin in SecurityManager?

我有一个用于可信应用程序代码的 ClassLoader 和一个用于用户提交(不可信)代码的单独的 ClassLoader。

我希望安全管理器限制用户提交的代码。如何从 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.");
        }
    }
});

我已经尝试过的:

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

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.");
        }
    }
});

所有不受信任的 classes 的规范名称都必须以 'nl' 开头。这是 afaik 从 class 上下文中获取调用者 class 的唯一方法,因为调用者 class 的数组位置未知,上下文数组的最后一个元素始终是class 使用 main 方法。

首先,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 保单 class。 (private static final ProtectionDomain MY_PD = MyPolicy.class.getProtectionDomain())
  • 如果检查是针对您自己的 ProtectionDomain,请使用快速路径。在这种情况下不要调用其他代码,否则你可能会得到一个 Whosebug。