Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/393.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 为JVM插件寻找安全的沙盒API_Java_Plugins - Fatal编程技术网

Java 为JVM插件寻找安全的沙盒API

Java 为JVM插件寻找安全的沙盒API,java,plugins,Java,Plugins,当您有一个应用程序服务器,并且希望在其中运行第三方插件时,您可以使用限制性的安全管理器来阻止它们执行类似System.exit()的操作,但这只是问题的一半。那些不受信任的插件仍然可以进入无限循环,或者在你有时间眨眼之前吃掉所有的空闲堆。Thread.stop()已被弃用,因此您不能只杀死一个amok线程,而且由于堆是共享的,插件不仅会在耗尽所有堆时出现OutOfMemoryError,而且所有其他正在运行的线程也会出现OutOfMemoryError 是否有一些开源应用程序/API/框架可以操

当您有一个应用程序服务器,并且希望在其中运行第三方插件时,您可以使用限制性的安全管理器来阻止它们执行类似System.exit()的操作,但这只是问题的一半。那些不受信任的插件仍然可以进入无限循环,或者在你有时间眨眼之前吃掉所有的空闲堆。Thread.stop()已被弃用,因此您不能只杀死一个amok线程,而且由于堆是共享的,插件不仅会在耗尽所有堆时出现OutOfMemoryError,而且所有其他正在运行的线程也会出现OutOfMemoryError

是否有一些开源应用程序/API/框架可以操纵插件类的字节码,使线程可终止和/或跟踪分配,以便在分配太多时终止线程?即使代码不容易“打包”以便“单独使用”。您可以通过插入可以随意产生异常(由另一个“管理器”线程触发)的代码使线程可终止,并确保插件不会捕获异常。您还可以添加一些计数器来计算调用和循环的数量以及分配的数量,并让“管理器”线程杀死一个突破配置限制的插件

我认为所有这些事情都可以用ASM来完成,但我希望它们以前都做过。我可以让插件在它们自己的JVM中运行,但这将涉及大量的数据不断封送/解封,如果插件JVM死亡/崩溃,我仍然不知道潜在的几十个(100个?)插件中的哪一个是问题所在,我不可能每个插件运行一个JVM

我发现了一些相关的问题,但没有一个可以解决无限循环和占用堆的问题:


我一直在使用OSGi框架将插件支持添加到现有的web应用程序中。根据我工作和阅读本主题的经验,我了解到:

1) OSGi是JVM上最著名和最受支持的插件标准。这种规范有多种不同的实现方式,如Eclipse(Eclipse)、菲利克斯(Apache)、动态模块(Spring)等。因此,大量的开源基础工作在这背后。

2) 规范中没有提到资源约束。事实上,他们一直在积极避免谈论这件事。这并不是说他们不知道,但他们的立场是,在JVM上,你无法阻止人们做一些伤害。因此,JVM上插件规范的黄金标准并没有提到这一点

关于如何实现这些约束,有很多信息(如您发布的链接),但在防止恶意插件做坏事方面,您无能为力

这意味着,没有捷径可以阻止资源占用(CPU、内存、文件描述符、SQL连接等)

堆和CPU是最简单的。只是做一个“System.exec('rm-rf')”怎么样?或者打开64000个套接字,并可能停止创建任何新的套接字


出现问题的方式太多了,试图为JVM设计一个进程中的沙箱来允许插件几乎是不可能的。

我找到了一个非常简单的解决“System.exec('rm-rf*)”问题的方法:

package de.unkrig.commons.lang.security;
导入java.security.AccessControlContext;
导入java.security.Permission;
导入java.security.Permissions;
导入java.security.ProtectionDomain;
导入java.util.Collections;
导入java.util.HashMap;
导入java.util.Map;
导入java.util.WeakHashMap;
导入de.unkrig.commons.nullanalysis.Nullable;
/**
*此类建立一个安全管理器,该管理器限制通过特定类执行的代码的权限,
*可以由类、类名和/或类装入器指定。
*
*“通过类执行”意味着执行堆栈包含该类。例如,如果类{@code a}的方法
*调用类{@code B}的方法,然后调用类{@code C}的方法,这三个类都是
*以前{@link#conmited(Class,Permissions)conmited},然后对于类{@code C}执行的所有操作
*三个{@link Permissions}的交集适用。
*
*类、类名或类装入器的权限一旦被限制,就不能更改;这防止了任何
*试图(例如,封闭类本身)释放封闭。
*
*代码示例:
* 
*Runnable unprivileged=new Runnable(){
*公开募捐{
*System.getProperty(“user.dir”);
*      }
*  };
*
*//不受限制地跑。
*非特权。运行();//很好。
*
*//设置最严格的权限。
*限制(unprivileged.getClass(),new Permissions());
*非特权。运行();//抛出SecurityException。
*
*//尝试更改权限。
*  {
*权限=新权限();
*permissions.add(newallpermission());
*Sandbox.confine(unprivileged.getClass(),permissions);//引发SecurityException。
*  }
*unprivileged.run();
* 
*/
公开决赛
类沙箱{
私有沙盒(){}

私有静态最终映射首先,类似System.exec()的东西使用适当配置的类加载器和SecurityManager可以防止IO。插件根本无法使用它无法加载的类,无论它多么努力。看看它:它抛出了一个SecurityException!阻止访问JVM之外的资源是很容易的,因为安全性已经集成到JVM中了。其次,我不同意你的观点,如果你不能解决所有的问题,你就根本不应该尝试。事实上,小偷可以用电锯把我的后门打开,这并不是让它不锁的理由。
package de.unkrig.commons.lang.security;

import java.security.AccessControlContext;
import java.security.Permission;
import java.security.Permissions;
import java.security.ProtectionDomain;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.WeakHashMap;

import de.unkrig.commons.nullanalysis.Nullable;

/**
 * This class establishes a security manager that confines the permissions for code executed through specific classes,
 * which may be specified by class, class name and/or class loader.
 * <p>
 * To 'execute through a class' means that the execution stack includes the class. E.g., if a method of class {@code A}
 * invokes a method of class {@code B}, which then invokes a method of class {@code C}, and all three classes were
 * previously {@link #confine(Class, Permissions) confined}, then for all actions that are executed by class {@code C}
 * the <i>intersection</i> of the three {@link Permissions} apply.
 * <p>
 * Once the permissions for a class, class name or class loader are confined, they cannot be changed; this prevents any
 * attempts (e.g. of the confined class itself) to release the confinement.
 * <p>
 * Code example:
 * <pre>
 *  Runnable unprivileged = new Runnable() {
 *      public void run() {
 *          System.getProperty("user.dir");
 *      }
 *  };
 *
 *  // Run without confinement.
 *  unprivileged.run(); // Works fine.
 *
 *  // Set the most strict permissions.
 *  Sandbox.confine(unprivileged.getClass(), new Permissions());
 *  unprivileged.run(); // Throws a SecurityException.
 *
 *  // Attempt to change the permissions.
 *  {
 *      Permissions permissions = new Permissions();
 *      permissions.add(new AllPermission());
 *      Sandbox.confine(unprivileged.getClass(), permissions); // Throws a SecurityException.
 *  }
 *  unprivileged.run();
 * </pre>
 */
public final
class Sandbox {

    private Sandbox() {}

    private static final Map<Class<?>, AccessControlContext>
    CHECKED_CLASSES = Collections.synchronizedMap(new WeakHashMap<Class<?>, AccessControlContext>());

    private static final Map<String, AccessControlContext>
    CHECKED_CLASS_NAMES = Collections.synchronizedMap(new HashMap<String, AccessControlContext>());

    private static final Map<ClassLoader, AccessControlContext>
    CHECKED_CLASS_LOADERS = Collections.synchronizedMap(new WeakHashMap<ClassLoader, AccessControlContext>());

    static {

        // Install our custom security manager.
        if (System.getSecurityManager() != null) {
            throw new ExceptionInInitializerError("There's already a security manager set");
        }
        System.setSecurityManager(new SecurityManager() {

            @Override public void
            checkPermission(@Nullable Permission perm) {
                assert perm != null;

                for (Class<?> clasS : this.getClassContext()) {

                    // Check if an ACC was set for the class.
                    {
                        AccessControlContext acc = Sandbox.CHECKED_CLASSES.get(clasS);
                        if (acc != null) acc.checkPermission(perm);
                    }

                    // Check if an ACC was set for the class name.
                    {
                        AccessControlContext acc = Sandbox.CHECKED_CLASS_NAMES.get(clasS.getName());
                        if (acc != null) acc.checkPermission(perm);
                    }

                    // Check if an ACC was set for the class loader.
                    {
                        AccessControlContext acc = Sandbox.CHECKED_CLASS_LOADERS.get(clasS.getClassLoader());
                        if (acc != null) acc.checkPermission(perm);
                    }
                }
            }
        });
    }

    // --------------------------

    /**
     * All future actions that are executed through the given {@code clasS} will be checked against the given {@code
     * accessControlContext}.
     *
     * @throws SecurityException Permissions are already confined for the {@code clasS}
     */
    public static void
    confine(Class<?> clasS, AccessControlContext accessControlContext) {

        if (Sandbox.CHECKED_CLASSES.containsKey(clasS)) {
            throw new SecurityException("Attempt to change the access control context for '" + clasS + "'");
        }

        Sandbox.CHECKED_CLASSES.put(clasS, accessControlContext);
    }

    /**
     * All future actions that are executed through the given {@code clasS} will be checked against the given {@code
     * protectionDomain}.
     *
     * @throws SecurityException Permissions are already confined for the {@code clasS}
     */
    public static void
    confine(Class<?> clasS, ProtectionDomain protectionDomain) {
        Sandbox.confine(
            clasS,
            new AccessControlContext(new ProtectionDomain[] { protectionDomain })
        );
    }

    /**
     * All future actions that are executed through the given {@code clasS} will be checked against the given {@code
     * permissions}.
     *
     * @throws SecurityException Permissions are already confined for the {@code clasS}
     */
    public static void
    confine(Class<?> clasS, Permissions permissions) {
        Sandbox.confine(clasS, new ProtectionDomain(null, permissions));
    }

    // Code for 'CHECKED_CLASS_NAMES' and 'CHECKED_CLASS_LOADERS' omitted here.

}