加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Java > 正文

java – 为JVM插件寻找安全的Sandbox API

发布时间:2020-12-15 02:31:54 所属栏目:Java 来源:网络整理
导读:如果您有一个应用程序服务器,并且想要在其中运行第三方插件,则可以使用限制性安全管理器来阻止它们执行System.exit()等操作,但这只是故事的一半.那些不受信任的插件仍然可以进入无限循环,或者在你有时间眨眼之前吃完所有的空闲堆. Thread.stop()已被弃用,所
如果您有一个应用程序服务器,并且想要在其中运行第三方插件,则可以使用限制性安全管理器来阻止它们执行System.exit()等操作,但这只是故事的一半.那些不受信任的插件仍然可以进入无限循环,或者在你有时间眨眼之前吃完所有的空闲堆. Thread.stop()已被弃用,所以你不能只是杀死一个amok线程,并且由于堆是共享的,因此插件不仅会在它耗尽所有堆时获得OutOfMemoryError,但所有其他线程也会运行.

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

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

我找到了一些相关的问题,但没有一个解决无限循环和吃堆的问题:

> Sandbox JVM to secure server from untrusted sources
> How do i create a java sandbox?
> SecurityManager for a cloud service “sandbox”

解决方法

我找到了’System.exec(‘rm -rf *’)’问题的一个非常简单的解决方案:

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.

}

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读