Как мне создать песочницу Java? - PullRequest
46 голосов
/ 11 ноября 2009

Я хочу, чтобы мое приложение запускало чужой код, или плагины. Тем не менее, какие варианты я должен сделать это, чтобы они не писали вредоносный код. Как мне контролировать то, что они могут или не могут сделать?

Я наткнулся на то, что в JVM есть функция «встроенной в песочницу» - что это и единственный способ? Существуют ли сторонние библиотеки Java для создания песочницы?

Какие варианты у меня есть? Ссылки на руководства и примеры приветствуются!

Ответы [ 6 ]

23 голосов
/ 11 ноября 2009

Вы ищете менеджер безопасности . Вы можете ограничить разрешения приложения, указав policy .

18 голосов
/ 11 ноября 2009
  • Определение и регистрация собственного менеджера безопасности позволит вам ограничить действия кода - см. Документацию оракула для SecurityManager .

  • Также рассмотрим , создающий отдельный механизм для загрузки кода - то есть вы можете написать или создать экземпляр другого Classloader для загрузки кода из специального места. У вас может быть соглашение для загрузки кода - например, из специального каталога или из специально отформатированного zip-файла (как WAR-файлы и JAR-файлы). Если вы пишете загрузчик классов, это ставит вас в положение необходимости выполнять работу для загрузки кода. Это означает, что если вы видите что-то (или некоторую зависимость), которую хотите отклонить, вы можете просто не загрузить код. http://java.sun.com/javase/6/docs/api/java/lang/ClassLoader.html

5 голосов
/ 13 июня 2013

Взгляните на проект java-sandbox , который позволяет легко создавать очень гибкие песочницы для запуска ненадежного кода.

4 голосов
/ 11 ноября 2009

Для приложений AWT / Swing вам необходимо использовать нестандартный класс AppContext, который может измениться в любое время. Таким образом, чтобы быть эффективным, вам нужно запустить другой процесс для запуска подключаемого кода и иметь дело с взаимодействием между ними (немного похоже на Chrome). Для процесса плагина потребуется набор SecurityManager и ClassLoader, чтобы изолировать код плагина и применить соответствующий ProtectionDomain к классам плагина.

3 голосов
/ 15 июня 2014

Вот как можно решить проблему с помощью SecurityManager:

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

<code>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();
 * 
* / публичный финал песочница класса { личная песочница () {} приватная статическая финальная карта , AccessControlContext> CHECKED_CLASSES = Collections.synchronizedMap (новый WeakHashMap , AccessControlContext> ()); частная статическая конечная карта CHECKED_CLASS_NAMES = Collections.synchronizedMap (new HashMap ()); приватная статическая финальная карта CHECKED_CLASS_LOADERS = Collections.synchronizedMap (new WeakHashMap ()); static { // Установить наш собственный менеджер безопасности. if (System.getSecurityManager ()! = null) { бросить новый ExceptionInInitializerError («Менеджер безопасности уже настроен»); } System.setSecurityManager (new SecurityManager () { @ Overide public void checkPermission (@Nullable Permission perm) { утверждать Пермь! = ноль; for (Class <?> clasS: this.getClassContext ()) { // Проверяем, был ли установлен ACC для класса. { AccessControlContext acc = Sandbox.CHECKED_CLASSES.get (clasS); if (acc! = null) acc.checkPermission (perm); } // Проверяем, был ли установлен ACC для имени класса. { AccessControlContext acc = Sandbox.CHECKED_CLASS_NAMES.get (clasS.getName ()); if (acc! = null) acc.checkPermission (perm); } // Проверяем, был ли установлен ACC для загрузчика классов. { AccessControlContext acc = Sandbox.CHECKED_CLASS_LOADERS.get (clasS.getClassLoader ()); if (acc! = null) acc.checkPermission (perm); } } } }); } // -------------------------- / ** * Все будущие действия, которые выполняются с помощью {@code clasS}, будут проверены на соответствие с {@code * accessControlContext}. * * @throws SecurityException Разрешения уже ограничены для {@code clasS} * / публичная статическая пустота ограничение (класс <?> clasS, AccessControlContext accessControlContext) { if (Sandbox.CHECKED_CLASSES.containsKey (clasS)) { выбросить новое SecurityException («Попытка изменить контекст управления доступом для« + clasS + »); } Sandbox.CHECKED_CLASSES.put (clasS, accessControlContext); } / ** * Все будущие действия, которые выполняются с помощью {@code clasS}, будут проверены на соответствие с {@code * protectionDomain}. * * @throws SecurityException Разрешения уже ограничены для {@code clasS} * / публичная статическая пустота ограничение (класс <?> clasS, ProtectionDomain protectionDomain) { Sandbox.confine ( учебный класс, новый AccessControlContext (новый ProtectionDomain [] {protectionDomain}) ); } / ** * Все будущие действия, которые выполняются с помощью {@code clasS}, будут проверены на соответствие с {@code * разрешения}. * * @throws SecurityException Разрешения уже ограничены для {@code clasS} * / публичная статическая пустота ограничение (класс <?> clasS, права доступа) { Sandbox.confine (clasS, новый ProtectionDomain (ноль, разрешения)); } // Код для CHECKED_CLASS_NAMES и CHECKED_CLASS_LOADERS здесь опущен. }
0 голосов
/ 11 декабря 2017

Дискуссия по этому вопросу вдохновила меня на запуск собственного проекта песочницы.

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

В нем я наткнулся на важный секретный вопрос: «Как вы разрешаете коду вне песочницы обходить SecurityManager

Я помещаю код песочницы в свою собственную группу ThreadGroup и всегда предоставляю разрешение за пределами этой группы. Если вам все равно нужно запустить привилегированный код в этой группе (например, в обратном вызове), вы можете использовать ThreadLocal, чтобы установить флаг только для этого потока. Загрузчик классов не позволит песочнице получить доступ к ThreadLocal. Кроме того, если вы делаете это, вам нужно запретить использование финализаторов, так как они запускаются в отдельном потоке вне ThreadGroup.

...