Перейти к основному содержимому

Введение в Java SecurityManager

· 4 мин. чтения

1. Обзор

В этом руководстве мы рассмотрим встроенную инфраструктуру безопасности Java, которая по умолчанию отключена. В частности, мы рассмотрим его основные компоненты, точки расширения и конфигурации.

2. SecurityManager в действии

Это может быть сюрпризом, но настройки SecurityManager по умолчанию запрещают многие стандартные операции :

System.setSecurityManager(new SecurityManager());
new URL("http://www.google.com").openConnection().connect();

Здесь мы программно включаем контроль безопасности с настройками по умолчанию и пытаемся подключиться к google.com.

Затем мы получаем следующее исключение:

java.security.AccessControlException: access denied ("java.net.SocketPermission"
"www.google.com:80" "connect,resolve")

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

3. Вариант использования

Эта инфраструктура безопасности доступна начиная с Java 1.0. Это было время, когда апплеты — Java-приложения, встроенные в браузер, — были довольно распространены. Естественно, нужно было ограничить их доступ к системным ресурсам.

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

Например, предположим, что у нас есть экземпляр Tomcat, где сторонние клиенты могут размещать свои веб-приложения. Мы не хотим позволять им выполнять такие операции, как System.exit() , потому что это повлияет на другие приложения и, возможно, на всю среду.

4. Дизайн

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

Одним из основных компонентов встроенной инфраструктуры безопасности является java.lang SecurityManager . У него есть несколько методов checkXxx, таких как checkConnect , который разрешал нашу попытку подключения к Google в тесте выше. Все они делегируются методу checkPermission(java.security.Permission) .

4.2. Разрешение

Экземпляры java.security.Permission обозначают запросы авторизации. Стандартные классы JDK создают их для всех потенциально опасных операций (таких как чтение/запись файла, открытие сокета и т. д.) и передают их SecurityManager для соответствующей авторизации.

4.3. Конфигурация

Мы определяем разрешения в специальном формате политики. Эти разрешения принимают форму записей о предоставлении :

grant codeBase "file:${{java.ext.dirs}}/*" {
permission java.security.AllPermission;
};

Приведенное выше правило codeBase является необязательным. Мы можем вообще не указывать поле или использовать signedBy (интегрированный с соответствующими сертификатами в хранилище ключей) или принципал ( java.security.Principal, присоединенный к текущему потоку через javax.security.auth.Subject ). Мы можем использовать любую комбинацию этих правил .

По умолчанию JVM загружает общий файл системной политики, расположенный по адресу < java.home>/lib/security/java.policy . Если мы определили какую-либо локальную политику пользователя в <user.home>/.java.policy , JVM добавит ее к системной политике.

Также можно указать файл политики через командную строку: – Djava.security.policy=/my/policy-file . Таким образом, мы можем добавлять политики к ранее загруженным системным и пользовательским политикам.

Существует специальный синтаксис для замены всех системных и пользовательских политик (если они есть) — двойной знак равенства: — Djava.security.policy==/my/policy-file

5. Пример

Давайте определим пользовательское разрешение:

public class CustomPermission extends BasicPermission {
public CustomPermission(String name) {
super(name);
}

public CustomPermission(String name, String actions) {
super(name, actions);
}
}

и общий сервис, который должен быть защищен:

public class Service {

public static final String OPERATION = "my-operation";

public void operation() {
SecurityManager securityManager = System.getSecurityManager();
if (securityManager != null) {
securityManager.checkPermission(new CustomPermission(OPERATION));
}
System.out.println("Operation is executed");
}
}

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

java.security.AccessControlException: access denied
("com.foreach.security.manager.CustomPermission" "my-operation")

at java.security.AccessControlContext.checkPermission(AccessControlContext.java:472)
at java.security.AccessController.checkPermission(AccessController.java:884)
at java.lang.SecurityManager.checkPermission(SecurityManager.java:549)
at com.foreach.security.manager.Service.operation(Service.java:10)

Мы можем создать наш файл <user.home>/.java.policy со следующим содержимым и попробовать перезапустить приложение:

grant codeBase "file:<our-code-source>" {
permission com.foreach.security.manager.CustomPermission "my-operation";
};

Теперь все работает отлично.

6. Заключение

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

Как обычно, полный исходный код этой статьи доступен на GitHub .