我目前正在开发一个小型Java应用程序,其中必须与不受信任的代码一起运行受信任的代码.为了实现这一点,我已经安装了一个自定义SecurityManager
,它会SecurityException
在检查权限时抛出.
作为可信代码和不可信代码之间的桥梁,我有一个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. */ Constructorctor; 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.(ClassLoader.java:316) at sun.reflect.DelegatingClassLoader. (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)
根据Javadoc的RuntimePermission
说法,createClassLoader
许可是一个有风险的许可:
这是非常危险的许可.可以实例化自己的类加载器的恶意应用程序然后可以将自己的恶意类加载到系统中.这些新加载的类可以由类加载器放入任何保护域,从而自动授予类该域的权限.
我有两个问题:
具体是什么造成了这个错误?为什么在第16次,我得到一个类加载器的请求?我怀疑这与Java试图通过生成字节码来直接实例化对象来优化反射有关,但我不确定.
如果没有将createClassLoader
特权列入白名单,这是危险的,有没有办法从可信代码中实例化不受信任的对象?
我从根本上以错误的方式接近这个吗?
谢谢!