如何创建Java沙盒?

如何创建Java沙盒?,java,security,plugins,sandbox,Java,Security,Plugins,Sandbox,我想让我的应用程序运行其他人的代码,也就是插件。但是,我有什么办法可以确保安全,这样他们就不会编写恶意代码了。我如何控制他们能做什么或不能做什么 我偶然发现JVM有一个“内置沙盒”特性——这是什么?这是唯一的方法吗?是否有用于制作沙盒的第三方Java库 我有什么选择?链接到指南和例子是感激的 您正在寻找一个新的解决方案。您可以通过指定一个来限制应用程序的权限。 定义和注册您自己的安全管理器将允许您限制代码的功能-有关详细信息,请参阅oracle文档 >P>也可以考虑创建一个单独的加载代码的机制

我想让我的应用程序运行其他人的代码,也就是插件。但是,我有什么办法可以确保安全,这样他们就不会编写恶意代码了。我如何控制他们能做什么或不能做什么

我偶然发现JVM有一个“内置沙盒”特性——这是什么?这是唯一的方法吗?是否有用于制作沙盒的第三方Java库

我有什么选择?链接到指南和例子是感激的

您正在寻找一个新的解决方案。您可以通过指定一个来限制应用程序的权限。

  • 定义和注册您自己的安全管理器将允许您限制代码的功能-有关详细信息,请参阅oracle文档

  • >P>也可以考虑<强>创建一个单独的加载代码的机制,即可以编写或实例化另一个类加载器,以便从一个特殊的地方加载代码。您可能有一个加载代码的约定,例如从特殊目录或特殊格式的zip文件(如WAR文件和JAR文件)加载代码。如果您正在编写一个类加载器,它会让您处于必须完成工作才能加载代码的境地。这意味着,如果您看到要拒绝的内容(或依赖项),则可能无法加载代码


对于AWT/Swing应用程序,您需要使用非标准的
AppContext
类,该类可以随时更改。因此,为了有效,您需要启动另一个进程来运行插件代码,并处理两者之间的通信(有点像Chrome)。插件过程将需要一个
SecurityManager
集合和一个
ClassLoader
来隔离插件代码,并对插件类应用适当的
ProtectionDomain

看看可以轻松创建非常灵活的沙盒来运行不受信任的代码。

以下是解决问题的方法使用SecurityManager:

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();
* 
*/
公开决赛
类沙箱{
私有沙盒(){}

私人静态最终地图关于这个问题的讨论启发我启动了自己的沙箱项目

在这篇文章中,我遇到了一个重要的安全问题:“如何允许沙箱之外的代码绕过
SecurityManager
?”


我将沙盒代码放在它自己的线程组中,并且在该组之外时总是授予权限,您可以使用ThreadLocal仅为该线程设置标志。类加载器将阻止沙箱访问ThreadLocal。此外,如果您这样做,您需要禁止使用终结器,因为它们在ThreadGroup之外的专用线程中运行。

在深入研究Java安全API一天后,我发现了一个惊人的发现在受权限限制的沙箱中执行不受信任代码的简单解决方案:

以下是(简化的)源代码:

package org.codehaus.commons.compiler;
导入java.security.AccessControlContext;
导入java.security.AccessController;
导入java.security.Permission;
导入java.security.PermissionCollection;
导入java.security.Policy;
导入java.security.PrivilegedAction;
导入java.security.PrivilegedActionException;
导入java.security.PrivilegedExceptionAction;
导入java.security.ProtectionDomain;
公开决赛
类沙箱{
静止的{
if(System.getSecurityManager()==null){
//在安装安全管理器之前,请配置适当的(“积极”)策略。
Policy.setPolicy(新策略(){
@重写公共布尔值
暗示(ProtectionDomain域,权限){return true;}
});
System.setSecurityManager(新的SecurityManager());
}
}
私有最终访问控制上下文访问控制上下文;
/**
*@param权限将应用于以后对{@link#confine(PrivilegedAction)}和{@link的调用
*#限制(特权)
*/
公众的
沙箱(许可证)
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.

}
package org.codehaus.commons.compiler;

import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.Permission;
import java.security.PermissionCollection;
import java.security.Policy;
import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.security.ProtectionDomain;

public final
class Sandbox {

    static {

        if (System.getSecurityManager() == null) {

            // Before installing the security manager, configure a decent ("positive") policy.
           Policy.setPolicy(new Policy() {

                @Override public boolean
                implies(ProtectionDomain domain, Permission permission) { return true; }
            });

            System.setSecurityManager(new SecurityManager());
        }
    }

    private final AccessControlContext accessControlContext;

    /**
     * @param permissions Will be applied on later calls to {@link #confine(PrivilegedAction)} and {@link
     *                    #confine(PrivilegedExceptionAction)}
     */
    public
    Sandbox(PermissionCollection permissions) {
        this.accessControlContext = new AccessControlContext(new ProtectionDomain[] {
            new ProtectionDomain(null, permissions)
        });
    }

    /**
     * Runs the given <var>action</var>, confined by the permissions configured through the {@link
     * #Sandbox(PermissionCollection) constructor}.
     *
     * @return The value returned by the <var>action</var>
     */
    public <R> R
    confine(PrivilegedAction<R> action) {
        return AccessController.doPrivileged(action, this.accessControlContext);
    }

    public <R> R
    confine(PrivilegedExceptionAction<R> action) throws Exception {
        try {
            return AccessController.doPrivileged(action, this.accessControlContext);
        } catch (PrivilegedActionException pae) {
            throw pae.getException();
        }
    }
}