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 .