Java 在SecurityManager下创建对象时出现NoClassDefFoundError

Java 在SecurityManager下创建对象时出现NoClassDefFoundError,java,security,securitymanager,Java,Security,Securitymanager,我试图通过在一个非常严格的SecurityManager下运行处理用户提供内容的代码来保护我的应用程序。它是AccessController.doPrivileged()的头部,通常用于提供具有额外权限的代码块,但我使用它将代码块约束到一个非常小的沙箱中 在我需要调用构造函数之前,一切都很好。然后我需要阅读世界。(或者至少对我的类路径上的所有.jar、.class和.property文件)我不能只授予读取权限,因为这是我试图避免的。(例如,尝试读取/etc/passwd的XEE攻击)我认为这还不

我试图通过在一个非常严格的SecurityManager下运行处理用户提供内容的代码来保护我的应用程序。它是AccessController.doPrivileged()的头部,通常用于提供具有额外权限的代码块,但我使用它将代码块约束到一个非常小的沙箱中

在我需要调用构造函数之前,一切都很好。然后我需要阅读世界。(或者至少对我的类路径上的所有.jar、.class和.property文件)我不能只授予读取权限,因为这是我试图避免的。(例如,尝试读取/etc/passwd的XEE攻击)我认为这还不够

我可以将一些构造函数移出SecurityManager块,但有些是不可避免的,因为它是一个SAX解析器,需要在遍历树时创建对象


想法?

我找到了90%的答案。简单的回答是,我们需要设置很多权限,但是这个过程很明显,并且很容易隐藏在某些实用程序类中。如果能在标准的地方买到,那就太好了,不过哦,好吧。唉

我们需要做的就是检查classpath和extdir的系统属性(并为它们添加FilePermissions(只读)),进行必要的SecurityPermissions(只读),最后为所有系统属性添加PropertyPermissions(只读)。剩下的只是一些非常明显的权限,例如,授予临时目录的r/w/d(但不是执行)权限,授予本地主机的“解析”访问权限,等等

一个真正安全的沙盒可能不想让所有的系统属性都可读,但这很容易修复,留给读者

public class LoggingSecurityManager extends SecurityManager {
    private AccessControlContext ctx;
    private Properties properties = new Properties;
    private Set missingProperties = new HashSet();

    public LoggingSecurityManager() {
        properties.add(
            new FilePermission(System.get("java.io.tmpdir") + "/-", "read,write,delete"));

        // maybe...
        properties.add(
            new FilePermission(System.get("user.home") + "/-", "read,write,delete"));

        addSystemPropertyPermissions();
        addSecurityPermissions();
        addClassPathPermissions();
        addOtherPropertyPermissions();

        permissions.add(new RuntimePermission("accessClassInPackage.sun.reflect"));
        permissions.add(new RuntimePermission("accessClassInPackage.sun.jdbc.odbc"));
        permissions.add(new RuntimePermission("accessClassInPackage.sun.security.provider"));
        permissions.add(new SocketPermission("localhost", "resolve"));
        permissions.add(new NetPermission("getProxySelector"));

        ctx = new AccessControlContext(new ProtectionDomain[] {
            new ProtectionDomain(null, permissions)
        });
    }

    /**
     * Add read-only permission to read system properties.
     * We may want to filter this list to remove sensitive information
     */
    public void addSystemPropertyPermissions() {
        for (Object key : Collections.list(System.getProperties().keys())) {
            permissions.add(new PropertyPermission((String) key, "read"));
        }
    }

    /**
     * Add read-only permissions for initializing security.
     */
    public void addSecurityPermissions() {
        permissions.add(new SecurityPermission("getPolicy"));
        permissions.add(new SecurityPermission("getProperty.random.source"));
        permissions.add(new SecurityPermission("getProperty.securerandom.source"));

        for (int i = 1; i < 10; i++) { // configurable limit?
            permissions.add(new SecurityPermission("getProperty.security.provider." + i));
        }

        String s = Security.getProperty("securerandom.source");
        if ((s != null) && s.startsWith("file:/")) {
            permissions.add(new FilePermission(s.substring(5), "read"));
        }

        // should have been covered already but wasn't....
        permissions.add(new FilePermission("/dev/random", "read"));
      }

    /**
     * Add read-only permissions for everything on classpath.
     */
    public void addClassPathPermissions() {
        permissions.add(new FilePermission(String.format("%/lib/-",
            System.getProperty("java.home")), "read"));

        // add standard class path.
        String pathSep = System.getProperty("path.separator");
        for (String entry : System.getProperty("java.class.path").split(pathSep)) {
            File f = new File(entry);
            if (f.isFile()) {
                permissions.add(new FilePermission(entry, "read"));
            } else if (f.isDirectory()) {
                permissions.add(new FilePermission(String.format("%s/-", entry), "read"));
            } // or could be neither fish nor fowl
        }

        // add endorsed extensions.
        for (String dir : System.getProperty("java.ext.dirs").split(pathSep)) {
            permissions.add(new FilePermission(String.format("%s/-", dir), "read"));
        }
    }

    /**
     * Add other standard properties.
     */
    public void addOtherPropertyPermissions() {
        permissions.add(new PropertyPermission("jdbc.drivers", "read"));
        permissions.add(new PropertyPermission("java.security.egd", "read"));
        permissions.add(new PropertyPermission("socksProxyHost", "read"));
    }
}

我还没有决定最好的行动方案。我想我可以做的是重写checkPermission方法,并在看到前两个权限(至少)时查看调用堆栈。如果它们来自JDK的深处,它们可能是安全的。如果它们来自用户代码,则可能不确定。

不确定我是否100%遵循该场景,但问题是否不是构造函数,而是类加载(通常在第一次引用给定类时发生)?如果是这样的话,您可以简单地在doPrivileged块之外引用所需的类,并将构造函数留在其中。甚至像“新字符串(foo)”这样的东西。(我知道,很愚蠢,但我一直在想一些绝对有效的方法。)
    // ------------ S C A R Y - B L O C K -----------
    permissions.add(new ReflectPermission("suppressAccessChecks"));  (!!)
    permissions.add(new RuntimePermission("createClassLoader")); (!!)
    permissions.add(new SecurityPermission("putProviderProperty.SUN"));
    permissions.add(new RuntimePermission("readFileDescriptor"));**
    permissions.add(new RuntimePermission("writeFileDescriptor"));
    // ------------ S C A R Y - B L O C K -----------