当前位置: 首页 > 面试题库 >

Java安全性:通过URLClassLoader加载的沙盒插件

谢正初
2023-03-14
问题内容

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

编辑:更新以响应AniB。

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

我的应用程序具有一个插件机制,第三方可以提供一个JAR,该JAR包含实现特定接口的类。使用URLClassLoader,我可以加载该类并实例化它,没问题。因为该代码可能不受信任,所以我需要防止其行为异常。例如,我在单独的线程中运行插件代码,以便在进入无限循环或花费太长时间时可以杀死它。但是,试图为他们设置一个安全沙箱,使他们无法执行诸如建立网络连接或访问硬盘驱动器上的文件之类的事情,这使我非常高兴。我的努力总是导致对插件不起作用(它具有与应用程序相同的权限),或者也限制了应用html" target="_blank">程序。我希望主应用程序代码能够执行几乎所有想要的操作,

关于该主题的文档和在线资源是复杂,混乱和矛盾的。我已经读过很多地方(例如该问题),我需要提供一个自定义的SecurityManager,但是当我尝试它时,我遇到了问题,因为JVM延迟加载了JAR中的类。所以我可以实例化它,但是如果我在加载的对象上调用一个方法来实例化同一JAR中的另一个类,则该方法会爆炸,因为它拒绝了从JAR中读取的权利。

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

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

主应用程序(受信任)

PluginTest.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
                html" target="_blank">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;
    }
}

PluginThread.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;
    }
}

问题答案:

从文档:
The AccessControlContext of the thread that created the instance of URLClassLoader will be used when subsequently loading classes and resources.

The classes that are loaded are by default granted permission only to access the URLs specified when the URLClassLoader was created.

URLClassLoader完全按照其说的做,AccessControlContext是您需要查看的内容。基本上,在AccessControlContext中引用的线程没有权限执行您认为做的事情。



 类似资料:
  • 我在尝试首次登录 Apple Chat Business Sandbox 时看到以下内容: 微调器在没有任何响应的情况下继续旋转... 这在Safari和Chrome浏览器中都有发生。 缺少了什么?

  • 问题内容: Java应该没有内存泄漏,但是仍然可能。当我的程序出现内存泄漏时,我可以修复它(我希望)。但是,当某些第三方程序包具备该功能时,我该怎么办?几乎什么都没有,除非不使用此软件包。 还有其他解决方案吗?我喜欢沙盒的想法。您被允许在某个区域内做任何您想做的事情,而您的“身体上的”没有能力打扰其他人。有没有办法为Java中的内存使用创建此类沙箱?想象一下=创建用于内存使用的沙箱,允许某些程序包

  • 创建一个渲染器可运行于 Chromium OS 沙盒中的浏览器窗口. 启用此选项后,渲染器必须通过IPC与主进程进行通信才能访问Node API。同时,为了使Chromium OS沙盒可运行,electron 必须使用 --enable-sandbox命令行参数运行。 Chromium主要的安全功能之一是所有Blink渲染或JavaScript都运行在沙盒中,该沙盒使用了特定于操作系统的功能以确保

  • MIP 以浏览体验与加载速度为优先考量点,因此在组件开发的时候,MIP 只开放了部分原生 JS 供组件开发者使用,以尽量避免组件开发出有悖 MIP 站点体验的组件。这个实现部分开放原生 JS API 的机制就是 MIP 的加载机制。 部分开放的 JS API MIP 鼓励使用 JS 进行计算,进行逻辑实现等等。因此这类工具型 API、数据结构对象等等具有完全的功能,比如 Math.*、Array、

  • 我有一个页面,上面有一些D3 javascript。这个页面位于HTTPS网站内,但证书是自签名的。 当我加载页面时,我的D3可视化没有显示,我得到了错误: 混合内容:页面位于'https://integration.jsite.com/data/vis'通过HTTPS加载,但请求了不安全的XMLHttpRequestendpoint'http://integration.jsite.com/da

  • 当前的增删改查,无论登陆与否都可以操作,实在太不靠谱了,所以,还是加个检查吧. 判断用户登陆 UserModule添加一个注解 @Filters(@By(type=CheckSession.class, args={"me", "/"})) 含义是,如果当前Session没有带me这个attr,就跳转到/页面,即首页. 同时,为login方法设置为空的过滤器,不然就没法登陆了 @Filters(