Sandbox przeciwko złośliwemu kodowi w aplikacji Java

W środowisku serwera symulacji, gdzie użytkownicy mogą przesyłać własny kod, który ma być uruchomiony przez serwer, byłoby wyraźnie korzystne, aby każdy kod przesłany przez użytkownika był uruchamiany w side a sandbox, w przeciwieństwie do apletów znajdujących się w przeglądarce. Chciałem być w stanie wykorzystać sam JVM, zamiast dodawać kolejną warstwę maszyny Wirtualnej, aby wyizolować te przesłane komponenty.

Tego rodzaju ograniczenie wydaje się być możliwe przy użyciu istniejącego modelu Java sandbox, ale czy istnieje dynamiczny sposób aby umożliwić to tylko dla zgłoszonych przez użytkownika części uruchomionej aplikacji?

Author: Alan Krueger, 2009-02-02

7 answers

  1. Uruchom niezaufany kod we własnym wątku. To na przykład zapobiega problemom z nieskończonymi pętlami i tym podobne, i ułatwia przyszłe kroki. Niech główny wątek czeka na zakończenie wątku, a jeśli trwa zbyt długo, zabij go nitką.przestań. Nić.stop jest przestarzały, ale ponieważ niezaufany kod nie powinien mieć dostępu do żadnych zasobów, można go bezpiecznie zabić.

  2. Ustawia SecurityManager w tym wątku. Utworzyć podklasę SecurityManager, która nadpisuje checkPermission (Permission perm) aby po prostu rzucić SecurityException dla wszystkich uprawnień z wyjątkiem kilku wybranych. Istnieje lista metod i uprawnień, których wymagają: uprawnienia w Java TM 6 SDK .

  3. Użyj niestandardowego Classloadera, aby załadować niezaufany kod. Twój class loader zostanie wywołany dla wszystkich klas, których używa niezaufany kod, więc możesz zrobić takie rzeczy, jak wyłączyć dostęp do poszczególnych klas JDK. Rzecz do zrobienia jest posiadanie białej listy dozwolonych klas JDK.

  4. Możesz chcieć uruchomić niezaufany kod w oddzielnym JVM. Podczas gdy poprzednie kroki uczyniłyby kod bezpiecznym, jest jedna irytująca rzecz, którą izolowany kod może nadal zrobić: przydzielić tyle pamięci, ile może, co powoduje wzrost widocznego śladu głównej aplikacji.

JSR 121: specyfikacja api izolacji aplikacji została zaprojektowana, aby rozwiązać ten problem, ale niestety nie ma wdrożenie jeszcze.

To dość szczegółowy temat i głównie piszę to wszystko z głowy.

Ale w każdym razie, jakiś niedoskonały, użyj-na-własne-ryzyko, prawdopodobnie buggy (pseudo) kod:

ClassLoader

class MyClassLoader extends ClassLoader {
  @Override
  public Class<?> loadClass(String name) throws ClassNotFoundException {
    if (name is white-listed JDK class) return super.loadClass(name);
    return findClass(name);
  }
  @Override
  public Class findClass(String name) {
    byte[] b = loadClassData(name);
    return defineClass(name, b, 0, b.length);
  }
  private byte[] loadClassData(String name) {
    // load the untrusted class data here
  }
}

SecurityManager

class MySecurityManager extends SecurityManager {
  private Object secret;
  public MySecurityManager(Object pass) { secret = pass; }
  private void disable(Object pass) {
    if (pass == secret) secret = null;
  }
  // ... override checkXXX method(s) here.
  // Always allow them to succeed when secret==null
}

wątek

class MyIsolatedThread extends Thread {
  private Object pass = new Object();
  private MyClassLoader loader = new MyClassLoader();
  private MySecurityManager sm = new MySecurityManager(pass);
  public void run() {
    SecurityManager old = System.getSecurityManager();
    System.setSecurityManager(sm);
    runUntrustedCode();
    sm.disable(pass);
    System.setSecurityManager(old);
  }
  private void runUntrustedCode() {
    try {
      // run the custom class's main method for example:
      loader.loadClass("customclassname")
        .getMethod("main", String[].class)
        .invoke(null, new Object[]{...});
    } catch (Throwable t) {}
  }
}
 104
Author: waqas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-02-18 21:23:29

Oczywiście taki schemat budzi różnego rodzaju obawy dotyczące bezpieczeństwa. Java ma rygorystyczne ramy bezpieczeństwa, ale nie jest trywialna. Nie należy przeoczyć możliwości jej przekręcenia i umożliwienia nieuprzywilejowanemu Użytkownikowi dostępu do ważnych elementów systemu.

Pomijając to Ostrzeżenie, jeśli bierzesz wkład użytkownika w postaci kodu źródłowego, pierwszą rzeczą, którą musisz zrobić, to skompilować go do kodu bajtowego Javy. AFIAK, nie da się tego zrobić natywnie, więc trzeba będzie wykonać wywołanie systemowe do javaca i skompiluj kod źródłowy do bajtowego kodu na dysku. Oto tutorial, który może być użyty jako punkt wyjścia do tego. Edit : Jak się dowiedziałem w komentarzach, w rzeczywistości można kompilować kod Java ze źródła natywnie używając javax.narzędzia.JavaCompiler

Gdy masz kod bajtowy JVM, możesz go załadować do JVM za pomocą Classloadera defineClass function. Aby ustawić kontekst bezpieczeństwa dla tej załadowanej klasy, musisz określić ProtectionDomain . Minimalny konstruktor dla ProtectionDomain wymaga zarówno kodu źródłowego, jak iPermissionCollection . PermissionCollection jest dla Ciebie obiektem podstawowego użytku - możesz go użyć do określenia dokładnych uprawnień, jakie posiada załadowana Klasa. Uprawnienia te powinny być ostatecznie egzekwowane przez AccessController JVM.

Istnieje wiele możliwych punktów błędu tutaj, i należy być bardzo ostrożnym, aby całkowicie zrozum wszystko, zanim cokolwiek wdrożysz.

 18
Author: shsmurfy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-02-03 22:29:17

The Java-Sandbox jest biblioteką do wykonywania kodu Java z ograniczonym zestawem uprawnień. Może być używany do zezwalania na dostęp tylko do zestawu klas i zasobów z białej listy. Nie wydaje się możliwość ograniczenia dostępu do poszczególnych metod. Wykorzystuje system z niestandardową klasą loader i menedżer bezpieczeństwa, aby to osiągnąć.

Nie używałem go, ale wygląda dobrze zaprojektowany i dość dobrze udokumentowany. @Waqas dał bardzo ciekawą odpowiedź wyjaśniającą jak to jest możliwe, aby wdrożyć siebie. Ale o wiele bezpieczniej jest pozostawić taki krytyczny i złożony kod ekspertom.

Zauważ jednak, że projekt nie był aktualizowany od 2013 roku, a twórcy opisują go jako "eksperymentalny". Jego Strona główna zniknęła, ale wpis źródłowy pozostaje.

Przykładowy kod zaadaptowany ze strony projektu:

SandboxService sandboxService = SandboxServiceImpl.getInstance();

// Configure context 
SandboxContext context = new SandboxContext();
context.addClassForApplicationLoader(getClass().getName());
context.addClassPermission(AccessType.PERMIT, "java.lang.System");

// Whithout this line we get a SandboxException when touching System.out
context.addClassPermission(AccessType.PERMIT, "java.io.PrintStream");

String someValue = "Input value";

class TestEnvironment implements SandboxedEnvironment<String> {
    @Override
    public String execute() throws Exception {
        // This is untrusted code
        System.out.println(someValue);
        return "Output value";
    }
};

// Run code in sandbox. Pass arguments to generated constructor in TestEnvironment.
SandboxedCallResult<String> result = sandboxService.runSandboxed(TestEnvironment.class, 
    context, this, someValue);

System.out.println(result.get());
 10
Author: Lii,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-31 17:26:11

Cóż, jest bardzo późno, aby podać jakieś sugestie lub rozwiązania, ale wciąż miałem do czynienia z podobnym rodzajem problemu, trochę bardziej zorientowane na badania. Zasadniczo starałem się zapewnić przepis i automatyczne oceny dla zadań programistycznych dla kursu Java na platformach e-learningowych.

  1. jednym ze sposobów może być utworzenie oddzielnej maszyny Wirtualnej (nie JVM), ale rzeczywistej maszyny Wirtualnej z minimalną konfiguracją możliwego systemu operacyjnego dla każdego ucznia.
  2. Zainstaluj JRE dla Javy lub bibliotek zgodnie z Twoimi językami programowania, niezależnie od tego, co chcesz, aby uczniowie kompilowali i wykonywali na tych maszynach.

Wiem, że brzmi to dość skomplikowanie i wiele zadań, ale Oracle Virtual Box już dostarcza Java API do dynamicznego tworzenia lub klonowania maszyn wirtualnych. https://www.virtualbox.org/sdkref/index.html (uwaga, nawet VMware dostarcza również API do tego samego)

A dla minimalnej wielkości i konfiguracji dystrybucji Linuksa możesz odnieść się do tej tutaj http://www.slitaz.org/en/,

Więc teraz, jeśli uczniowie bałagan lub próbuje to zrobić, może być z pamięci lub systemu plików lub sieci, Gniazdo, maksymalnie może uszkodzić własną maszynę wirtualną.

Również wewnętrznie w tych maszynach wirtualnych można zapewnić dodatkowe zabezpieczenia, takie jak Sandbox (security manager ) dla Javy lub tworzenie kont specyficznych dla użytkowników w Linuksie, a tym samym ograniczanie dostępu.

Mam nadzieję, że to pomoże !!

 4
Author: Shrikant Havale,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-12 23:51:06

Oto bezpieczne rozwiązanie problemu:

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.

}

Proszę o komentarz!

CU

Arno

 2
Author: Arno Unkrig,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-15 09:18:39

Aby rozwiązać problem w zaakceptowanej odpowiedzi, w której niestandardowe SecurityManager będzie miało zastosowanie do wszystkich wątków w JVM, zamiast na zasadzie dla każdego wątku, możesz utworzyć niestandardowe SecurityManager, które można włączyć / wyłączyć dla określonych wątków w następujący sposób:

import java.security.Permission;

public class SelectiveSecurityManager extends SecurityManager {

  private static final ToggleSecurityManagerPermission TOGGLE_PERMISSION = new ToggleSecurityManagerPermission();

  ThreadLocal<Boolean> enabledFlag = null;

  public SelectiveSecurityManager(final boolean enabledByDefault) {

    enabledFlag = new ThreadLocal<Boolean>() {

      @Override
      protected Boolean initialValue() {
        return enabledByDefault;
      }

      @Override
      public void set(Boolean value) {
        SecurityManager securityManager = System.getSecurityManager();
        if (securityManager != null) {
          securityManager.checkPermission(TOGGLE_PERMISSION);
        }
        super.set(value);
      }
    };
  }

  @Override
  public void checkPermission(Permission permission) {
    if (shouldCheck(permission)) {
      super.checkPermission(permission);
    }
  }

  @Override
  public void checkPermission(Permission permission, Object context) {
    if (shouldCheck(permission)) {
      super.checkPermission(permission, context);
    }
  }

  private boolean shouldCheck(Permission permission) {
    return isEnabled() || permission instanceof ToggleSecurityManagerPermission;
  }

  public void enable() {
    enabledFlag.set(true);
  }

  public void disable() {
    enabledFlag.set(false);
  }

  public boolean isEnabled() {
    return enabledFlag.get();
  }

}

ToggleSecurirtyManagerPermission jest tylko prostą implementacją java.security.Permission w celu zapewnienia, że tylko autoryzowany kod może włączyć/wyłączyć menedżera zabezpieczeń. Wygląda to tak:

import java.security.Permission;

public class ToggleSecurityManagerPermission extends Permission {

  private static final long serialVersionUID = 4812713037565136922L;
  private static final String NAME = "ToggleSecurityManagerPermission";

  public ToggleSecurityManagerPermission() {
    super(NAME);
  }

  @Override
  public boolean implies(Permission permission) {
    return this.equals(permission);
  }

  @Override
  public boolean equals(Object obj) {
    if (obj instanceof ToggleSecurityManagerPermission) {
      return true;
    }
    return false;
  }

  @Override
  public int hashCode() {
    return NAME.hashCode();
  }

  @Override
  public String getActions() {
    return "";
  }

}
 2
Author: alphaloop,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-26 02:32:08

Prawdopodobnie będziesz musiał użyć niestandardowego SecurityManger i/lub AccessController . Wiele szczegółów można znaleźć w Java Security Architecturei other security documentation firmy Sun.

 0
Author: Kieron,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-02-02 05:44:16