如何在 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.");
}
}
});
我已经尝试过的:
StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE).getCallerClass().getClassLoader()
首先检查权限,因此会出现堆栈溢出异常。
Thread.currentThread().getStackTrace()[2].getClassLoaderName()
是不安全的,因为如果不受信任的加载程序的规范名称与受信任的相同,它只提供 class 加载程序名称而不是 class 对象loader 那么这是一个安全问题。
我找到了一个临时解决方案,但它并不完美:
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。
我有一个用于可信应用程序代码的 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.");
}
}
});
我已经尝试过的:
StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE).getCallerClass().getClassLoader()
首先检查权限,因此会出现堆栈溢出异常。Thread.currentThread().getStackTrace()[2].getClassLoaderName()
是不安全的,因为如果不受信任的加载程序的规范名称与受信任的相同,它只提供 class 加载程序名称而不是 class 对象loader 那么这是一个安全问题。
我找到了一个临时解决方案,但它并不完美:
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。