Лучшие практики песочницы Groovy Shell

#java #groovy #sandbox #groovyshell

#java #groovy #Песочница #groovyshell

Вопрос:

Я пытаюсь настроить песочницу Groovy Shell, которая может выполнять ненадежный код. Эти ненадежные коды предоставляются конечными пользователями (разработчиками) в качестве конфигураций поведения, например, как определить, является ли человек состоятельным. Итак, они действительно являются частью основной программы. Мне нужно убедиться, что я не уязвим для любого плохого кода [например, бесконечного цикла] / взломов.

Я понимаю, что здесь играют роль две вещи:

  1. Виртуальная машина Java, предоставляющая среду выполнения.
  2. Оболочка Groovy, которая интерпретирует и выполняет код.

Существуют ли рекомендации по изолированию Groovy Shell?

Спасибо

Комментарии:

1. Вы имеете в виду, что где-то в базе данных или файле conf вводится пользователь def isHighNetWorth(...) { ... } , и вы хотели бы прочитать это из db / conf и выполнить его?

Ответ №1:

В итоге я создал файл политики. Что-то вроде этого:

 grant codeBase "file:/your jar file" {
  permission java.security.AllPermissions;
}

grant codeBase "file:/groovy/shell" {
}

grant codeBase "file:/groovy/script" {
}
 

Когда Groovy выполняется в интерпретируемом режиме, кодовая база является либо file:/groovy/shell или file:/groovy/script . Вы можете предоставить определенные разрешения для любого контекста. Эти разрешения (или их отсутствие) не зависят от того, что вы предоставляете своей основной программе.

В дополнение к файлу политики, есть много других соображений.

  1. Что вы вкладываете в контекст оценки? Если вы разместите стороннюю библиотеку, у них может даже не быть надлежащей проверки разрешений.
  2. Некоторые системные вызовы, скажем System.out.println() , также не имеют проверки разрешений. Итак, возможно, вам также нужна проверка исходного кода (Дженкинс делает это).
  3. Чтобы ограничить процессор, вам может потребоваться запустить сценарий Groovy в отдельном потоке.
  4. Вероятно, вы тоже хотите ограничить возможности Groovy script import . Этого можно достичь с помощью Groovy ImportCustomizer .

Я написал статью: Безопасное выполнение скрипта Groovy в изолированной среде, чтобы обобщить свои выводы. Я надеюсь, что это поможет и другим.

Комментарии:

1. Ограничение импорта бесполезно. Вам не нужно импортировать класс, чтобы использовать его (ни в Java, ни в Groovy). Кроме того, Groovy предоставляет некоторые средства динамического получения ссылки, например, String.class.forName('sun.misc.Unsafe') .

2. Запуск скрипта в отдельном потоке в целом имеет смысл, но нет способа ограничить ресурсы (ни процессор, ни память).

Ответ №2:

Просто полагаться на диспетчер безопасности не решает всех проблем. Существует несколько причин, по которым Security Manager не подходит. Самое главное, что он основан на предположении, что критические методы уже включают надлежащие проверки разрешений. Как уже указывалось, это не всегда так. В одном только Java SE API есть множество примеров; расширяя область действия до дополнительных сторонних библиотек, вы редко увидите хорошие реализации, выполняющие проверки разрешений. Это не может быть модифицировано. Security Manager был разработан для защиты настольной системы; в отличие от этого, в многопользовательской системе требуется дополнительная защита от атак на других пользователей.

Рабочий подход — это то, что делает Дженкинс: перехватывать все вызовы методов и обращения к членам и проверять по настроенному белому списку. Это реализовано в https://github.com/jenkinsci/script-security-plugin

Здесь важно то, как поддерживать полезный белый список. Подход Дженкинса работает с обширными списками сигнатур методов (плюс черные списки). Хотя это консервативный подход, его, вероятно, утомительно поддерживать. Вы должны начать с наименьшего возможного подмножества API, которое вы хотите предложить. В лучшем случае существует специальный API для пользователей скриптов, который скрывает все детали реализации.

Все еще есть несколько угловых случаев, в частности, в отношении исчерпания ресурсов. Подумайте, например, о следующем сценарии:

 for( int x = 7; true; x *= x );
 

Этот бесконечный цикл будет держать процессор занятым, пока нет вызова метода или определения поля, которые должны быть перехвачены. ИМХО, лучшим способом справиться с этим было бы выполнить скрипт в отдельном потоке и остановить его по истечении заданного времени ожидания. Да, я имею в виду вызов Thread.stop() , потому что прерывания будут просто игнорироваться. Наконец, скрипты могут вызывать любые Throwable , даже проверенные, без их объявления; вы всегда должны использовать catch Throwable вокруг скрипта.

Комментарии:

1. Спасибо. Я тоже согласен с вашими оценками. Особенно в отношении ограничений, связанных с использованием менеджеров безопасности.

Ответ №3:

Немного больше контекста было бы полезно, но, учитывая то, что вы описали:

Я бы настоятельно рекомендовал запускать groovyshell внутри контейнера — по одному контейнеру на пользователя / экземпляр.

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

И если вы хотите пойти на крайние меры, вы можете легко запустить каждый контейнер в отдельной сети без каких-либо других узлов и без доступа в Интернет.

При этом плохой код будет ограничен уязвимостями docker, которые могут быть использованы из программы JVM.

Комментарии:

1. Спасибо за ответ. Я отредактировал вопрос, чтобы дать немного больше контекста: «… Эти ненадежные коды предоставляются конечными пользователями (разработчиками) в качестве конфигураций поведения, например, как определить, является ли человек состоятельным. Итак, они действительно являются частью основной программы … » У вас есть точка запуска GroovyShell внутри контейнера. Однако, несмотря на это, я бы не хотел, чтобы контейнер был скомпрометирован.