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

2024-01-03

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

编辑:更新以回应 Ani B.

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

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

有关该主题的文档和在线资源非常复杂、令人困惑且矛盾。我在很多地方读过(例如这个问题 https://stackoverflow.com/questions/502218/sandbox-against-malicious-code-in-a-java-application)我需要提供一个自定义的 SecurityManager,但是当我尝试它时,我遇到了问题,因为 JVM 延迟加载 JAR 中的类。所以我可以很好地实例化它,但如果我在加载的对象上调用一个方法来实例化同一个 JAR 中的另一个类,它就会崩溃,因为它被拒绝从 JAR 读取的权利。

理论上,我可以在 SecurityManager 中检查 FilePermission,看看它是否正在尝试从自己的 JAR 中加载。那很好,但是URLClassLoader 文档 http://download.oracle.com/javase/6/docs/api/说:“默认情况下,加载的类仅被授予访问创建 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();
}

插件安全管理器.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
    }
}

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

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

PluginThread 必须在插件安全管理器上设置 classSource 属性,这是类文件的路径。插件 SecurityManager 现在看起来像这样:

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 中引用的线程没有权限执行您认为它执行的操作。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java 安全性:通过 URLClassLoader 加载的沙箱插件 的相关文章

随机推荐

  • PHP 上的无限循环的 Pthreads 与 Parallel

    我正在寻找一种在 PHP 上执行多线程的方法 并遇到了 pthreads PHP API 我认为它很容易实现 但是我必须找出如何安装支持 Debian 的 ZTS 的 PHP 版本 问题是 当我查看 pthreads php net 文档时
  • 查找8086微处理器中的物理地址

    在8086微处理器中 20位地址分为16位 4位地址 其中4位二进制是段地址 当我们将4位二进制转换为十六进制时 它会变成1位十六进制 我的问题是当我们遇到计算物理地址的问题时从逻辑地址上看 给出的是一个4bit的十六进制段地址 为什么会这
  • 如何验证 Perl 中的函数参数?

    你能告诉我 Perl 中验证函数参数最明智的方法是什么吗 代码片段 sub testfunction my args my value args gt value die no value set process value testfun
  • Redux 和 React Router:结合调度和导航 (history.push)

    我对如何使用 React Router 有点一无所知history push route with Redux 换句话说 如果你将一个组件与 Redux 的连接mapDispatchToProps 你怎么放history push rout
  • 为什么 Flowable 不是 Observable

    为什么 Flowable 不是 Observable Observable 接口几乎是 Flowable 的子集 它们的实现几乎相同 为什么他们不实现一个通用接口 以便我们可以直接将 Flowable 转换为 Observable 为什么
  • 正则表达式匹配由任何字符分组包围的可选组

    我正在尝试匹配一个可选组 该组前面和后面可以有任意数量的字符 整个模式也有必需的开始和结束匹配 但中间的匹配是可选的 我从这个开始 当需要中间组时它有效 string text blah blah foo This is a test bl
  • Python Google Drive API - 获取“我的云端硬盘”文件夹的 ID

    在 Python 3 中使用 Drive API 我正在尝试编写一个脚本来下载整个用户的 Google Drive 此代码来自 Drive API V3 文档 经过修改以搜索文件夹而不是文件 将获取用户拥有所有权的每个文件夹 包括团队驱动器
  • 如何在 JSDoc 中将参数标记为包含 DOM 节点?

    我想指示参数应该是 DOM 节点 但我似乎找不到任何有关如何使用 JSDoc 指示这一点的信息 我可以用 Object 但这相当难看 我宁愿有类似的东西 Node or DOMNode 但我找不到任何例子来指出我的方向 那么 如何将参数标记
  • BlueZ 在 DBUS 上显示旧的缓存数据

    我对 BlueZ 有一个非常奇怪的问题 Ubuntu 16 04 中的库存版本 5 37 我正在开发蓝牙外围设备 我只有一套开发套件 在其固件中 我更改了广播的名称 当我使用时 hcitool lescan 显示我的设备的更新名称 但是当我
  • 使用 Collections API 进行随机播放

    我感到非常沮丧 因为我似乎无法弄清楚为什么集合洗牌无法正常工作 可以说我正在尝试洗牌randomizer大批 int randomizer new int 200 300 212 111 6 2332 Collections shuffle
  • 在 PhoneGap+jQuery Mobile 中使用 ajax 的 CORS 无法在设备上运行,但可以在浏览器上运行

    因此 我正在使用 Phone Gap 和 jQuery mobile 构建一个应用程序 当使用 ajax 从白名单服务器获取 json 响应时 我收到错误响应 但是控制台中没有显示任何内容 奇怪的是 当在网络浏览器中测试该应用程序时 它工作
  • 使用 Mongoose 处理 MongoDB 中查找、修改、保存流程中的冲突

    我想更新一个涉及读取其他集合和复杂修改的文档 因此更新运算符findAndModify 不能达到我的目的 这是我所拥有的 Collection findById id function err doc read from other col
  • 如何将消息从 WH_KEYBOARD_LL 转换为 corespondig unicode char

    你好 我正在编写一个多语言应用程序 并且正在使用WH KEYBOARD LL钩 我不在乎为什么要使用钩子 我尝试了多种方法 但这是最简单 最快的方法 现在在钩子过程中 LRESULT CALLBACK LowLevelKeyboardPro
  • 在每个视图 IOS 的底部显示音乐播放器 [关闭]

    Closed 这个问题需要细节或清晰度 help closed questions 目前不接受答案 我正在创建一个 IOS 应用程序 我想在每个视图的底部显示音乐播放器实例 Like this image 采用一个基本视图控制器 它将以编程
  • 使用 Robolectric 进行 Android http 测试

    我有一个 Android 应用程序 其中应用程序的主要部分是 APIcalls java 类 我在其中发出 http 请求以从服务器获取数据并在应用程序中显示数据 我想为这个 Java 类创建单元测试 因为它是应用程序的大部分 从服务器获取
  • 为什么 PreventDefault 不起作用?

    这是我的代码的一部分 如果我尝试将图像放在块上 preventDefault 不起作用 jQuery document ready function event props push dataTransfer imgDropzone on
  • Python SciPy 卷积与 fftconvolve

    我知道一般来说FFT and multiplication通常比直接更快convolve当数组比较大的时候进行操作 然而 我正在将一个很长的信号 比如 1000 万个点 与一个很短的响应 比如 1000 个点 进行卷积 在这种情况下fftc
  • 如何使用 execvp()

    用户将读取一行 我将保留第一个单词作为 execvp 的命令 假设他会打字 猫文件 txt 命令将是 cat 但我不知道如何使用这个execvp 我读了一些教程 但还是没明白 include
  • 依赖注入容器有什么好处?

    我了解依赖注入本身的好处 我们以 Spring 为例 我还了解其他 Spring 功能的好处 例如 AOP 不同类型的帮助器等 我只是想知道 XML 配置有什么好处 例如
  • Java 安全性:通过 URLClassLoader 加载的沙箱插件

    问题摘要 如何修改下面的代码 以便不受信任的动态加载代码在安全沙箱中运行 而应用程序的其余部分保持不受限制 为什么 URLClassLoader 不像它所说的那样处理它 编辑 更新以回应 Ani B 编辑 2 添加了更新的 PluginSe