it-swarm.com.de

Wie erstelle ich eine Java-Sandbox?

Ich möchte, dass meine Anwendung den Code anderer Leute ausführt, auch bekannt als Plugins. Welche Optionen muss ich jedoch festlegen, um diese Sicherheit zu gewährleisten, damit sie keinen schädlichen Code schreiben. Wie steuere ich, was sie können oder was nicht?

Ich bin gestolpert, dass JVM eine "eingebaute Sandbox" -Funktion hat - was ist das und ist dies der einzige Weg? Gibt es Java-Bibliotheken von Drittanbietern, um eine Sandbox zu erstellen?

Welche Möglichkeiten habe ich? Links zu Leitfäden und Beispielen sind willkommen!

45
corgrath

Sie suchen einen Sicherheitsmanager . Sie können die Berechtigungen einer Anwendung einschränken, indem Sie eine policy angeben.

23
tangens
  • Durch Definieren und Registrieren eines eigenen Sicherheitsmanagers können Sie die Funktionsweise des Codes einschränken. Weitere Informationen finden Sie in der Oracle-Dokumentation zu SecurityManager .

  • Berücksichtigen Sie auch Erstellen eines separaten Mechanismus zum Laden des Codes - d. H. Sie können einen anderen Classloader schreiben oder instanziieren , um den Code von einem bestimmten Ort aus zu laden. Möglicherweise haben Sie eine Konvention zum Laden des Codes - beispielsweise aus einem speziellen Verzeichnis oder aus einer speziell formatierten ZIP-Datei (als WAR-Dateien und JAR-Dateien). Wenn Sie einen Classloader schreiben, können Sie damit arbeiten, um den Code laden zu können. Das heißt, wenn Sie etwas (oder eine Abhängigkeit) sehen, das Sie ablehnen möchten, können Sie den Code einfach nicht laden. http://Java.Sun.com/javase/6/docs/api/Java/lang/ClassLoader.html

18
Dafydd Rees

Werfen Sie einen Blick auf das Java-Sandbox-Projekt , mit dem Sie sehr flexible Sandboxen erstellen können, um nicht vertrauenswürdigen Code auszuführen.

5
Arno Mittelbach

Für eine AWT/Swing-Anwendung müssen Sie eine nicht standardmäßige AppContext-Klasse verwenden, die sich jederzeit ändern kann. Um effektiv zu sein, müssen Sie einen anderen Prozess starten, um den Plug-in-Code auszuführen, und sich um die Kommunikation zwischen den beiden kümmern (etwas wie Chrome). Der Plug-In-Prozess benötigt eine SecurityManager-Gruppe und eine ClassLoader, um sowohl den Plug-In-Code zu isolieren als auch eine geeignete ProtectionDomain auf Plug-In-Klassen anzuwenden.

So kann das Problem mit einem SecurityManager gelöst werden:

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
Arno Unkrig

Die Diskussion zu dieser Frage inspirierte mich dazu, mein eigenes Sandbox-Projekt zu starten. 

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

Darin bin ich auf eine wichtige Sicherheitsfrage gestoßen: "Wie lässt man den Code außerhalb der Sandbox die SecurityManager umgehen?"

Ich habe den Sandbox-Code in seine eigene ThreadGroup eingefügt und erteile immer die Berechtigung, wenn er sich außerhalb dieser Gruppe befindet. Wenn Sie ohnehin privilegierten Code in dieser Gruppe ausführen müssen (beispielsweise in einem Rückruf), können Sie ein ThreadLocal verwenden, um nur für diesen Thread ein Flag zu setzen. Der Classloader verhindert, dass die Sandbox auf das ThreadLocal zugreift. Wenn Sie dies tun, müssen Sie auch die Verwendung von Finalizern verbieten, da sie in einem dedizierten Thread außerhalb der ThreadGroup ausgeführt werden.

0
Black Mantha