Java 安全 : Sandboxing plugins loaded via URLClassLoader

标签 java classloader securitymanager urlclassloader

问题摘要:如何修改下面的代码,使不受信任的动态加载代码在安全沙箱中运行,而应用程序的其余部分不受限制?为什么 URLClassLoader 不像它所说的那样处理它?<​​/h2>

编辑:更新以回应 Ani B。

编辑 2:添加了更新的 PluginSecurityManager。

我的应用程序有一个插件机制,第三方可以提供一个包含实现特定接口(interface)的类的 JAR。使用 URLClassLoader,我可以加载该类并实例化它,没问题。因为代码可能不受信任,所以我需要防止它行为不端。例如,我在一个单独的线程中运行插件代码,以便在它进入无限循环或时间过长时将其终止。但是试图为他们设置一个安全沙箱,这样他们就不能做诸如建立网络连接或访问硬盘上的文件之类的事情,这让我非常生气。我的努力总是导致要么对插件没有影响(它与应用程序具有相同的权限),要么还限制了应用程序。我希望主应用程序代码能够做任何它想做的事情,但插件代码被锁定。

有关该主题的文档和在线资源非常复杂、令人困惑且相互矛盾。我在不同的地方(例如 this question )读到我需要提供一个自定义的 SecurityManager,但是当我尝试它时我遇到了问题,因为 JVM 延迟加载 JAR 中的类。所以我可以很好地实例化它,但是如果我在加载的对象上调用从同一个 JAR 实例化另一个类的方法,它就会爆炸,因为它被拒绝从 JAR 读取的权利。

理论上,我可以在我的 SecurityManager 中检查 FilePermission 以查看它是否试图从自己的 JAR 中加载。没关系,但是 the URLClassLoader documentation说:“默认情况下,加载的类仅被授予访问创建 URLClassLoader 时指定的 URL 的权限。”那么为什么我什至需要一个自定义的 SecurityManager 呢? URLClassLoader 不应该只处理这个吗?为什么不呢?

这是一个重现问题的简化示例:

主应用程序(可信)

插件测试.java

package test.app;

import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;

import test.api.Plugin;

public class PluginTest {
    public static void pluginTest(String pathToJar) {
        try {
            File file = new File(pathToJar);
            URL url = file.toURI().toURL();
            URLClassLoader cl = new URLClassLoader(new java.net.URL[] { url });
            Class<?> clazz = cl.loadClass("test.plugin.MyPlugin");
            final Plugin plugin = (Plugin) clazz.newInstance();
            PluginThread thread = new PluginThread(new Runnable() {
                @Override
                public void run() {
                    plugin.go();
                }
            });
            thread.start();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}

插件.java

package test.api;

public interface Plugin {
    public void go();
}

PluginSecurityManager.java

package test.app;

public class PluginSecurityManager extends SecurityManager {
    private boolean _sandboxed;

    @Override
    public void checkPermission(Permission perm) {
        check(perm);
    } 

    @Override
    public void checkPermission(Permission perm, Object context) {
        check(perm);
    }

    private void check(Permission perm) {
        if (!_sandboxed) {
            return;
        }

        // I *could* check FilePermission here, but why doesn't
        // URLClassLoader handle it like it says it does?

        throw new SecurityException("Permission denied");
    }

    void enableSandbox() {
    _sandboxed = true;
    }

    void disableSandbox() {
        _sandboxed = false;
    }
}

插件线程.java

package test.app;

class PluginThread extends Thread {
    PluginThread(Runnable target) {
        super(target);
    }

    @Override
    public void run() {
        SecurityManager old = System.getSecurityManager();
        PluginSecurityManager psm = new PluginSecurityManager();
        System.setSecurityManager(psm);
        psm.enableSandbox();
        super.run();
        psm.disableSandbox();
        System.setSecurityManager(old);
    }
}

插件 JAR(不受信任)

MyPlugin.java

package test.plugin;

public MyPlugin implements Plugin {
    @Override
    public void go() {
        new AnotherClassInTheSamePlugin(); // ClassNotFoundException with a SecurityManager
        doSomethingDangerous(); // permitted without a SecurityManager
    }

    private void doSomethingDangerous() {
        // use your imagination
    }
}

更新: 我对其进行了更改,以便在插件代码即将运行之前通知 PluginSecurityManager 以便它知道它正在使用的类源。然后它将只允许对该类源路径下的文件进行文件访问。这也有一个很好的优势,我可以在我的应用程序开始时设置一次安全管理器,并在我输入和离开插件代码时更新它。

这几乎解决了问题,但没有回答我的另一个问题:为什么 URLClassLoader 不像它所说的那样为我处理这个问题?我将把这个问题留待一段时间,看看是否有人对这个问题有答案。如果是这样,该人将获得接受的答案。否则,我会将其授予 Ani B.,前提是 URLClassLoader 文档存在缺陷,并且他关于创建自定义 SecurityManager 的建议是正确的。

PluginThread 必须在 PluginSecurityManager 上设置 classSource 属性,这是类文件的路径。 PluginSecurityManager 现在看起来像这样:

package test.app;

public class PluginSecurityManager extends SecurityManager {
    private String _classSource;

    @Override
    public void checkPermission(Permission perm) {
        check(perm);
    } 

    @Override
    public void checkPermission(Permission perm, Object context) {
        check(perm);
    }

    private void check(Permission perm) {
        if (_classSource == null) {
            // Not running plugin code
            return;
        }

        if (perm instanceof FilePermission) {
            // Is the request inside the class source?
            String path = perm.getName();
            boolean inClassSource = path.startsWith(_classSource);

            // Is the request for read-only access?
            boolean readOnly = "read".equals(perm.getActions());

            if (inClassSource && readOnly) {
                return;
            }
        }

        throw new SecurityException("Permission denied: " + perm);
    }

    void setClassSource(String classSource) {
    _classSource = classSource;
    }
}

最佳答案

来自文档:
创建URLClassLoader实例的线程的AccessControlContext会在后续加载类和资源时使用。

默认情况下,加载的类仅被授予访问创建 URLClassLoader 时指定的 URL 的权限。

URLClassLoader 完全按照它所说的那样做,AccessControlContext 是您需要查看的内容。基本上,在 AccessControlContext 中引用的线程无权执行您认为的操作。

关于Java 安全 : Sandboxing plugins loaded via URLClassLoader,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3947558/

相关文章:

Java 包装比较

java - 将图形与关键监听器一起使用

jsf - 如何在 Liberty Profile 中重现相同的 WAS ND 类加载器设置

Java 策略文件 - 拒绝对代码库的权限

java - 独立 java 应用程序上的 Eclipse AST 变量绑定(bind)

java - 有没有办法让 Puppet 在成功部署后运行 Jenkins 作业?

java - 验证、方法执行和JIT编译过程中类加载的原因和跟踪

java - 是否保证为 AspectJ 方面调用静态初始化程序?

Java AccessControlException 尽管授予了适当的文件权限