【发布时间】:2013-12-29 22:27:45
【问题描述】:
我目前正在开发一个小型 Java 应用程序,在该应用程序中,受信任的代码必须与不受信任的代码一起运行。为此,我安装了一个自定义的SecurityManager,它会在任何时候检查权限时抛出SecurityExceptions。
作为可信和不可信代码之间的桥梁,我有一个线程使用Constructor.newInstance() 来实例化一个不可信类型的对象。在进行此调用时,安全管理器被配置为阻止所有内容。有趣的是,前 15 次我尝试使用 Constructor.newInstance() 创建对象,一切正常,但第 16 次我得到了 SecurityException。
我已经设法把它归结为一个简单的测试程序:
import java.lang.reflect.*;
import java.security.*;
public class Main {
/* Track how many instances have been created so that we can see when the exception
* is thrown.
*/
private static int numInstances = 0;
public Main() {
System.out.println("Number created: " + ++numInstances);
}
public static void main(String[] args) {
/* Get the constructor for Main so that we can instantiate everything
* later on.
*/
Constructor<Main> ctor;
try {
ctor = Main.class.getConstructor();
} catch (NoSuchMethodException e) {
e.printStackTrace();
return;
}
/* Install a super prohibitive security manager that disallows all operations. */
System.setSecurityManager(new SecurityManager() {
@Override
public void checkPermission(Permission p) {
/* Nothing is allowed - any permission check causes a security
* exception.
*/
throw new SecurityException("Not permitted: " + p);
}
});
/* Continuously create new Main objects. */
try {
while (true) {
ctor.newInstance();
}
} catch (Exception e) {
e.printStackTrace();
return;
}
}
}
此程序安装一个SecurityManager,其checkPermission 总是抛出异常,无论请求什么权限。然后它位于一个循环中并使用ctor.newInstance() 实例化一个无害的Main 对象,该对象打印出到目前为止生成的实例数。这个程序在我的系统上的输出如下:
Number created: 1
Number created: 2
Number created: 3
Number created: 4
Number created: 5
Number created: 6
Number created: 7
Number created: 8
Number created: 9
Number created: 10
Number created: 11
Number created: 12
Number created: 13
Number created: 14
Number created: 15
java.lang.SecurityException: Not permitted: ("java.lang.RuntimePermission" "createClassLoader")
at Main$1.checkPermission(Main.java:32)
at java.lang.SecurityManager.checkCreateClassLoader(SecurityManager.java:611)
at java.lang.ClassLoader.checkCreateClassLoader(ClassLoader.java:274)
at java.lang.ClassLoader.<init>(ClassLoader.java:316)
at sun.reflect.DelegatingClassLoader.<init>(ClassDefiner.java:72)
at sun.reflect.ClassDefiner$1.run(ClassDefiner.java:60)
at sun.reflect.ClassDefiner$1.run(ClassDefiner.java:58)
at java.security.AccessController.doPrivileged(Native Method)
at sun.reflect.ClassDefiner.defineClass(ClassDefiner.java:57)
at sun.reflect.MethodAccessorGenerator$1.run(MethodAccessorGenerator.java:399)
at sun.reflect.MethodAccessorGenerator$1.run(MethodAccessorGenerator.java:396)
at java.security.AccessController.doPrivileged(Native Method)
at sun.reflect.MethodAccessorGenerator.generate(MethodAccessorGenerator.java:395)
at sun.reflect.MethodAccessorGenerator.generateConstructor(MethodAccessorGenerator.java:94)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:48)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
at Main.main(Main.java:39)
根据the Javadoc for RuntimePermission,授予createClassLoader 权限是有风险的:
这是一个极其危险的授予权限。可以实例化它们自己的类加载器的恶意应用程序然后可以将它们自己的流氓类加载到系统中。类加载器可以将这些新加载的类放入任何保护域中,从而自动授予类对该域的权限。
我有两个问题:
具体是什么导致了这个错误?为什么在第 16 次时,我收到了一个类加载器的请求?我怀疑这与 Java 试图通过生成字节码来直接实例化对象来优化反射有关,但我不确定。
如果不将
createClassLoader权限列入白名单,这很危险,有没有办法从受信任的代码中实例化不受信任的对象?我从根本上是不是以错误的方式处理这个问题?
谢谢!
【问题讨论】:
-
我试过你的代码,同样的行为。如果删除 throw 异常并添加打印语句,
checkPermission在创建第 16 个实例之前会被调用 3 次。
标签: java security reflection sandbox securitymanager