Статьи

Песочница Java Code

В предыдущем посте мы рассмотрели защиту мобильного Java-кода . Один из вариантов для этого — запустить код в клетке или песочнице .

В этом посте рассматривается, как настроить такую ​​песочницу для приложений Java.

Менеджер по безопасности

Средство безопасности в Java, которое поддерживает песочницу, является java.lang.SecurityManager .

По умолчанию Java работает без SecurityManager , поэтому вы должны добавить код в свое приложение, чтобы включить его:

1
System.setSecurityManager(new SecurityManager());

Вы можете использовать стандартный SecurityManager или его потомка.

SecurityManager имеет несколько методов checkXXX() которые все пересылают в checkPermission(permission, context) . Этот метод вызывает AccessController для выполнения реальной работы (см. Ниже).

[ checkXXX() являются пережитком Java 1.1 .]

Если запрошенный доступ разрешен, checkPermission() возвращается спокойно. Если отказано, java.lang.SecurityException .

Код, который реализует песочницу, должен вызвать метод checkXXX перед выполнением чувствительной операции:

1
2
3
4
5
SecurityManager securityManager = System.getSecurityManager();
if (securityManager != null) {
  Permission permission = ...;
  securityManager.checkPermission(permission);
}

JRE содержит такой же код во многих местах .

права доступа

Разрешение представляет доступ к системному ресурсу.

Чтобы такой доступ был разрешен, соответствующее разрешение должно быть явно предоставлено (см. Ниже) коду, пытающемуся получить доступ.

Разрешения вытекают из java.security.Permission . У них есть имя и необязательный список действий (в виде разделенных запятыми строковых значений).

Java поставляется с кучей предопределенных разрешений, таких как FilePermission . Вы также можете добавить свои собственные разрешения .

Ниже приведено разрешение на read файла /home/remon/thesis.pdf :

1
2
Permission readPermission = new java.io.FilePermission(
    '/home/remon/thesis.pdf', 'read');

Вы можете предоставить часть разрешений кода, чтобы сделать что-нибудь и все, предоставив ему AllPermission . Это имеет тот же эффект, что и запуск без SecurityManager .

полисы

Разрешения предоставляются с использованием политик . Политика отвечает за определение того, имеет ли код разрешение на выполнение чувствительной к безопасности операции.

AccessController консультируется с Policy чтобы узнать, предоставлено ли Permission .

В каждый момент времени может использоваться только один объект Policy . Код приложения может создавать подклассы Policy для обеспечения пользовательской реализации .

Реализация Policy умолчанию использует файлы конфигурации для загрузки грантов. Существует один общесистемный файл политики и один (необязательный) файл политики пользователя .

Вы можете создать дополнительные файлы конфигурации политики с помощью программы PolicyTool . Каждый файл конфигурации должен быть закодирован в UTF-8.

По умолчанию коду не предоставляется никаких разрешений. Каждое утверждение grant добавляет некоторые разрешения. Предоставленные разрешения не могут быть отозваны.

Следующий фрагмент политики предоставляет код, полученный из разрешения на read каталога /home/remon/code/ для файла /home/remon/thesis.pdf :

1
2
3
4
grant codeBase 'file:/home/remon/code/-' {
    permission java.io.FilePermission '/home/remon/thesis.pdf',
        'read';
};

Обратите внимание, что часть, следующая за codeBase является URL-адресом , поэтому вы всегда должны использовать прямую косую черту, даже в системе Windows.

codeBase с завершающим / соответствует всем файлам классов (не JAR-файлам) в указанном каталоге. codeBase с завершающим codeBase /* соответствует всем файлам (как классам, так и JAR-файлам), содержащимся в этом каталоге. codeBase с завершающим codeBase /- сопоставляет все файлы (файлы классов и JAR) в каталоге и рекурсивно все файлы в подкаталогах, содержащихся в этом каталоге.

Для путей в правах доступа к файлам в системах Windows необходимо использовать двойную обратную косую черту ( \\ ), поскольку \ является escape-символом:

1
2
3
4
grant codeBase 'file:/C:/Users/remon/code/-' {
    permission java.io.FilePermission
        'C:\\Users\\remon\\thesis.pdf', 'read';
};

Для большей гибкости вы можете писать гранты с переменными частями. Мы уже видели подстановочные знаки codeBase . Вы также можете заменить системные свойства :

1
2
3
4
grant codeBase 'file:/${user.home}/code/-' {
    permission java.io.FilePermission
        '${user.home}${/}thesis.pdf', 'read';
};

Обратите внимание, что
${/} заменяется разделителем пути для вашей системы. Нет необходимости использовать это в
codeBase , так как это URL.

Подписанный код

Конечно, мы должны убедиться, что код, который мы используем, подписан , чтобы мы знали, что он на самом деле пришел от того, от кого, по нашему мнению, он получился.

Мы можем проверять подписи в наших политиках, используя предложение signedBy :

1
2
3
4
keystore 'my.keystore';
grant signedBy 'signer.alias', codeBase ... {
  ...
};

Этот фрагмент политики использует хранилище ключей с псевдонимом my.keystore для поиска сертификата открытого ключа с псевдонимом signer.alias .

Затем он проверяет, что исполняемый код был подписан закрытым ключом, соответствующим открытому ключу в найденном сертификате.

Может быть только одна запись keystore .

Комбинация предложений codeBase и signedBy указывает ProtectionDomain . Все классы в одном и том же ProtectionDomain имеют одинаковые разрешения.

Привилегированный код

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

Маркировка кода как привилегированного позволяет фрагменту доверенного кода временно разрешить доступ к большему количеству ресурсов, чем доступно непосредственно к коду, который его вызвал. Другими словами, система безопасности будет обрабатывать всех вызывающих абонентов так, как если бы они исходили из ProtectionDomain класса, который выполняет привилегированный вызов, но только на время привилегированного вызова.

Вы делаете код привилегированным, запустив его внутри AccessController.doPrivileged() :

1
2
3
4
5
6
AccessController.doPrivileged(new PrivilegedAction() {
  public Object run() {
    // ...privileged code goes here...
    return null;
  }
});

Сборка Песочницы

Теперь у нас есть все, что нужно для сборки нашей песочницы:

  1. Установите SecurityManager
  2. Подпишите приложение jars
  3. Предоставить весь код, подписанный нами AllPermission
  4. Добавьте проверки прав доступа в тех местах, где мобильный код может вызвать
  5. Запустите код после проверки разрешений в doPrivileged()

Я создал простой пример на GitHub .

Ссылка: песочница Java Code от нашего партнера JCG Ремона Синнема в блоге по разработке безопасного программного обеспечения .