java - 为什么我的自定义 SecurityManager 在第 16 次使用 Constructor.newInstance 创建对象时会导致异常?

标签 java security reflection sandbox securitymanager

我目前正在开发一个小型 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.
         */
        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 权限是有风险的:

This is an extremely dangerous permission to grant. Malicious applications that can instantiate their own class loaders could then load their own rogue classes into the system. These newly loaded classes could be placed into any protection domain by the class loader, thereby automatically granting the classes the permissions for that domain.

我有两个问题:

  1. 具体是什么导致了这个错误?为什么在第 16 次左右,我收到了类加载器请求?我怀疑这与 Java 试图通过生成字节码来直接实例化对象来优化反射有关,但我不确定。

  2. 如果不将 createClassLoader 权限列入白名单,这很危险,有没有办法从受信任的代码中实例化不受信任的对象?

  3. 我是否从根本上以错误的方式处理了这个问题?

谢谢!

最佳答案

查看 this at GrepCode :

72  private static int     inflationThreshold = 15;

15 是通货膨胀阈值的默认值,在 NativeConstructorAccessorImpl 中引入更积极的优化之前的反射调用计数。 :

47  if (++numInvocations > ReflectionFactory.inflationThreshold()) {
48 ConstructorAccessorImpl acc = (ConstructorAccessorImpl)
49 new MethodAccessorGenerator().
50 generateConstructor(c.getDeclaringClass(),
51 c.getParameterTypes(),
52 c.getExceptionTypes(),
53 c.getModifiers());
54 parent.setDelegate(acc);

并且该特定代码会导致实例化一个新的类加载器,从而导致您在第 16 次迭代时出现异常。字节码生成发生在 MethodAccessorGenerator 中类,这是最有趣的一点:

387  // Load class
388 vec.trim();
389 final byte[] bytes = vec.getData();
390 // Note: the class loader is the only thing that really matters
391 // here -- it's important to get the generated code into the
392 // same namespace as the target class. Since the generated code
393 // is privileged anyway, the protection domain probably doesn't
394 // matter.
395 return AccessController.doPrivileged(
396 new PrivilegedAction<MagicAccessorImpl>() {
397 public MagicAccessorImpl run() {
398 try {
399 return (MagicAccessorImpl)
400 ClassDefiner.defineClass
401 (generatedName,
402 bytes,
403 0,
404 bytes.length,
405 declaringClass.getClassLoader()).newInstance();
406 } catch (InstantiationException e) {
407 throw (InternalError)
408 new InternalError().initCause(e);
409 } catch (IllegalAccessException e) {
410 throw (InternalError)
411 new InternalError().initCause(e);
412 }
413 }
414 });

至于授予该权限,您仍然可以选择为您授予权限的代码仔细形成一个保护域,而无需将其授予外部代码。

关于java - 为什么我的自定义 SecurityManager 在第 16 次使用 Constructor.newInstance 创建对象时会导致异常?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20527776/

相关文章:

reflection - 如何列出具有特定原子的所有谓词?

java - 卡片布局 : How can I tell which card is visible?

java - 使用反射加载 Build.getRadioVersion() 时出现 NoSuchMethodException

java - Apache 四郎 : IllegalArgumentException upon login

linux - 为什么文件可能比其目录提供更多权限?

c# - 从名称中获取对象

java - GWT 客户端外部库

java - 如何将组合框的光标设置为特定索引?

iphone - 企业 iPhone 应用程序无线分发的安全性 - OTA iOS

c# - 如何使用给定的 Type 对象调用泛型方法?