有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

安全性如何创建Java沙盒?

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

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

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


共 (6) 个答案

  1. # 1 楼答案

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

  2. # 2 楼答案

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

    https://svn.code.sf.net/p/loggifier/code/trunk/de.unkrig.commons.lang/src/de/unkrig/commons/lang/security/Sandbox.java

    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.
    
    }
    
  3. # 3 楼答案

    看看the java-sandbox project,它允许轻松创建非常灵活的沙盒来运行不受信任的代码

  4. # 5 楼答案

    关于这个问题的讨论启发我启动了自己的沙箱项目

    https://github.com/Black-Mantha/sandbox

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

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

  5. # 6 楼答案

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

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