Рубрики
Без рубрики

Основы безопасности Java

Краткий и практический обзор основ безопасности Java.

Автор оригинала: Kumar Chandrakant.

1. Обзор

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

Безопасность-это обширная тема, охватывающая множество областей . Некоторые из них являются частью самого языка, например модификаторы доступа и загрузчики классов. Кроме того, другие доступны в качестве услуг, которые включают шифрование данных, безопасную связь, аутентификацию и авторизацию, и это лишь некоторые из них.

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

2. Особенности языка

Прежде всего, безопасность в Java начинается прямо на уровне языковых функций . Это позволяет нам писать безопасный код, а также извлекать выгоду из многих неявных функций безопасности:

  • Статическая типизация данных: Java-это статически типизированный язык, который уменьшает возможности обнаружения ошибок, связанных с типом, во время выполнения
  • Модификаторы доступа: Java позволяет нам использовать различные модификаторы доступа, такие как public и private, для управления доступом к полям, методам и классам
  • Автоматическое управление памятью: Java имеет управление памятью на основе сбора мусора , что освобождает разработчиков от управления этим вручную
  • Проверка байт-кода: Java является скомпилированным языком, что означает, что он преобразует код в байт-код, не зависящий от платформы, и среда выполнения проверяет каждый байт-код, загружаемый для выполнения

Это не полный список функций безопасности, которые предоставляет Java, но этого достаточно, чтобы дать нам некоторую уверенность!

3. Архитектура безопасности в Java

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

Основные принципы безопасности в Java определяются совместимыми и расширяемыми провайдерами реализациями . Конкретная реализация Провайдера может реализовывать некоторые или все службы безопасности.

Например, некоторые из типичных услуг, которые может реализовать Поставщик , являются:

  • Криптографические алгоритмы (такие как DSA, RSA или SHA-256)
  • Средства генерации, преобразования и управления ключами (например, для ключей, специфичных для алгоритма)

Java поставляется с многими встроенными поставщиками . Кроме того, приложение может настроить несколько поставщиков в порядке предпочтения.

Следовательно, платформа поставщика в Java ищет конкретную реализацию службы во всех поставщиках в порядке предпочтения , установленном для них.

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

4. Криптография

Криптография является краеугольным камнем функций безопасности в целом и в Java. Это относится к инструментам и методам безопасной связи в присутствии противников .

4.1. Криптография Java

Архитектура криптографии Java (JCA) предоставляет платформу для доступа и реализации криптографических функций в Java, включая:

  • Цифровые подписи
  • Дайджесты сообщений
  • Симметричные и асимметричные шифры
  • Коды аутентификации сообщений
  • Генераторы ключей и ключевые фабрики

Самое главное, что Java использует реализации Provider на основе для криптографических функций.

Кроме того, Java включает в себя встроенные поставщики для часто используемых криптографических алгоритмов, таких как RSA, DSA и AES, и это лишь некоторые из них. Мы можем использовать эти алгоритмы для обеспечения безопасности данных в состоянии покоя, в использовании или в движении.

4.2. Криптография на практике

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

Таким образом, одним из решений является шифрование паролей таким образом, чтобы процесс повторялся, но только в одну сторону. Этот процесс известен как криптографическая хэш-функция, и SHA1 является одним из таких популярных алгоритмов.

Итак, давайте посмотрим, как мы можем сделать это на Java:

MessageDigest md = MessageDigest.getInstance("SHA-1");
byte[] hashedPassword = md.digest("password".getBytes());

Здесь Дайджест сообщений – это криптографический сервис, который нас интересует. Мы используем метод getInstance (), чтобы запросить эту услугу у любого из доступных поставщиков безопасности .

5. Инфраструктура Открытых Ключей

Инфраструктура открытых ключей (PKI) относится к настройке , которая обеспечивает безопасный обмен информацией по сети с использованием шифрования с открытым ключом . Эта установка основана на доверии, которое создается между сторонами, участвующими в общении. Это доверие основано на цифровых сертификатах, выданных нейтральным и доверенным центром, известным как Центр сертификации (ЦС).

5.1. Поддержка PKI в Java

Платформа Java имеет API для облегчения создания, хранения и проверки цифровых сертификатов :

  • Хранилище ключей : Java предоставляет класс Хранилище ключей для постоянного хранения криптографических ключей и доверенных сертификатов. Здесь Хранилище ключей может представлять как хранилище ключей, так и файлы хранилища доверия . Эти файлы имеют одинаковое содержимое, но различаются по их использованию.
  • CertStore : Кроме того, Java имеет класс CertStore , который представляет собой общедоступное хранилище потенциально ненадежных сертификатов и списков отзыва. Нам нужно получить сертификаты и списки отзыва для построения пути к сертификату среди других способов использования .

Java имеет встроенное хранилище доверия под названием “cacerts” , которое содержит сертификаты для хорошо известных центров сертификации.

5.2. Инструменты Java для PKI

В Java есть несколько действительно удобных инструментов для облегчения доверенного общения:

  • Существует встроенный инструмент под названием “keytool” для создания и управления хранилищем ключей и хранилищем доверия
  • Существует также еще один инструмент “jarsigner”, который мы можем использовать для подписи и проверки файлов JAR

5.3. Работа с сертификатами на Java

Давайте посмотрим, как мы можем работать с сертификатами на Java, чтобы установить безопасное соединение с помощью SSL. Взаимно аутентифицированное SSL-соединение требует, чтобы мы сделали две вещи:

  • Настоящий сертификат — Нам необходимо предоставить действительный сертификат другой стороне в сообщении. Для этого нам нужно загрузить файл хранилища ключей, где у нас должны быть наши открытые ключи:
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
char[] keyStorePassword = "changeit".toCharArray();
try(InputStream keyStoreData = new FileInputStream("keystore.jks")){
    keyStore.load(keyStoreData, keyStorePassword);
}
  • Проверка сертификата — Нам также необходимо проверить сертификат, представленный другой стороной в сообщении. Для этого нам нужно загрузить хранилище доверия, где у нас должны быть ранее доверенные сертификаты от других сторон:
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
// Load the trust-store from filesystem as before

Нам редко приходится делать это программно и обычно передавать системные параметры в Java во время выполнения:

-Djavax.net.ssl.trustStore=truststore.jks 
-Djavax.net.ssl.keyStore=keystore.jks

6. Аутентификация

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

6.1. Аутентификация на Java

API Java использует подключаемые модули входа для предоставления различных и часто множественных механизмов аутентификации приложениям. LoginContext предоставляет эту абстракцию, которая, в свою очередь, ссылается на конфигурацию и загружает соответствующий модуль Login .

В то время как несколько поставщиков предоставляют свои модули входа в систему, Java имеет некоторые доступные по умолчанию для использования:

  • Krb5LoginModule , для проверки подлинности на основе Kerberos
  • JndiLoginModule , для аутентификации на основе имени пользователя и пароля, поддерживаемой хранилищем LDAP
  • KeyStoreLoginModule , для аутентификации на основе криптографического ключа

6.2. Вход в систему по примеру

Одним из наиболее распространенных механизмов аутентификации является имя пользователя и пароль. Давайте посмотрим, как мы можем достичь этого с помощью JndiLoginModule .

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

LoginContext loginContext = new LoginContext("Sample", new SampleCallbackHandler());
loginContext.login();

Здесь мы используем экземпляр LoginContext для выполнения входа в систему . LoginContext принимает имя записи в конфигурации входа в систему — в данном случае это “Образец”. Кроме того, мы должны предоставить экземпляр CallbackHandler , используя модуль Login , который взаимодействует с пользователем для получения таких сведений, как имя пользователя и пароль.

Давайте взглянем на нашу конфигурацию входа в систему:

Sample {
  com.sun.security.auth.module.JndiLoginModule required;
};

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

7. Безопасная Связь

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

7.1. Поддержка Java для безопасной связи

Java предоставляет API для защиты сетевой связи с шифрованием, целостностью сообщений и аутентификацией как клиента, так и сервера :

  • SSL/TLS: SSL и его преемник, TLS, обеспечивают безопасность ненадежной сетевой связи с помощью шифрования данных и инфраструктуры открытых ключей. Java обеспечивает поддержку SSL/TLS через SSLSocket , определенный в пакете ” java.security.ssl “.
  • SASL: Уровень простой аутентификации и безопасности (SASL) является стандартом для аутентификации между клиентом и сервером. Java поддерживает SASL как часть пакета ” java.security.sasl “.
  • GGS-API/Kerberos: Универсальный API службы безопасности (GSS-API) обеспечивает единый доступ к службам безопасности через различные механизмы безопасности, такие как Kerberos v5. Java поддерживает GSS-API как часть пакета ” java.security.jgss “.

7.2. SSL-связь в действии

Теперь давайте посмотрим, как мы можем открыть безопасное соединение с другими сторонами в Java с помощью SSLSocket:

SocketFactory factory = SSLSocketFactory.getDefault();
try (Socket connection = factory.createSocket(host, port)) {
    BufferedReader input = new BufferedReader(
      new InputStreamReader(connection.getInputStream()));
    return input.readLine();
}

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

Чтобы это работало должным образом, мы должны создать и настроить хранилище ключей и хранилище доверия , как мы видели ранее.

8. Контроль Доступа

Контроль доступа относится к защите конфиденциальных ресурсов, таких как файловая система или кодовая база, от несанкционированного доступа. Обычно это достигается путем ограничения доступа к таким ресурсам.

8.1. Управление доступом на Java

Мы можем добиться контроля доступа в Java с помощью классов Политики и разрешений , опосредованных через SecurityManager класс . Менеджер безопасности является частью пакета ” java.lang ” и отвечает за обеспечение проверки контроля доступа в Java.

Когда загрузчик классов загружает класс во время выполнения, он автоматически предоставляет некоторые разрешения по умолчанию классу, инкапсулированному в объекте Permission . Помимо этих разрешений по умолчанию, мы можем предоставить больше возможностей классу с помощью политик безопасности. Они представлены классом Policy .

Во время выполнения кода, если среда выполнения обнаруживает запрос на защищенный ресурс, Менеджер безопасности проверяет запрошенное Разрешение на соответствие установленной Политике через стек вызовов. Следовательно, он либо предоставляет разрешение, либо выдает SecurityException .

8.2. Инструменты Java для политики

Java имеет реализацию по умолчанию Policy , которая считывает данные авторизации из файла свойств. Однако записи политики в этих файлах политики должны быть в определенном формате.

Java поставляется с “инструментом политики”, графической утилитой для создания файлов политики.

8.3. Контроль Доступа С Помощью Примера

Давайте посмотрим, как мы можем ограничить доступ к ресурсу, такому как файл в Java:

SecurityManager securityManager = System.getSecurityManager();
if (securityManager != null) {
    securityManager.checkPermission(
      new FilePermission("/var/logs", "read"));
}

Здесь мы используем Security Manager для проверки нашего запроса на чтение файла, завернутого в FilePermission .

Но/| Менеджер безопасности делегирует этот запрос Контроллеру доступа . Контроллер доступа внутренне использует установленную Политику для принятия решения.

Давайте рассмотрим пример файла политики:

grant {
  permission 
    java.security.FilePermission
      <>, "read";
};

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

Стоит отметить, что Security Manager может не быть установлен по умолчанию в Java. Мы можем гарантировать это, всегда начиная Java с параметра:

-Djava.security.manager -Djava.security.policy=/path/to/sample.policy

9. XML-подпись

XML-подписи полезны для защиты данных и обеспечивают целостность данных . W3C предоставляет рекомендации по управлению XML-подписью. Мы можем использовать XML-подпись для защиты данных любого типа, например двоичных данных.

9.1. XML-подпись в Java

Java API поддерживает создание и проверку XML-подписей в соответствии с рекомендуемыми рекомендациями. API цифровой подписи Java XML инкапсулирован в пакет ” javax.xml.crypto “.

Сама подпись-это просто XML-документ. XML-подписи могут быть трех типов:

  • Отсоединенный: Этот тип подписи находится поверх данных, которые являются внешними по отношению к элементу подписи.
  • Охват: Этот тип подписи находится поверх данных, которые являются внутренними для элемента подписи.
  • Заключенный: Этот тип подписи находится над данными, содержащими сам элемент подписи.

Конечно, Java поддерживает создание и проверку всех вышеперечисленных типов XML-подписей.

9.2. Создание XML-подписи

Теперь мы закатаем рукава и сгенерируем XML-подпись для наших данных. Например, мы можем отправить XML-документ по сети. Следовательно, мы хотели бы, чтобы ваш получатель мог проверить его целостность .

Итак, давайте посмотрим, как мы можем достичь этого в Java:

XMLSignatureFactory xmlSignatureFactory = XMLSignatureFactory.getInstance("DOM");
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
documentBuilderFactory.setNamespaceAware(true);
 
Document document = documentBuilderFactory
  .newDocumentBuilder().parse(new FileInputStream("data.xml"));
 
DOMSignContext domSignContext = new DOMSignContext(
  keyEntry.getPrivateKey(), document.getDocumentElement());
 
XMLSignature xmlSignature = xmlSignatureFactory.newXMLSignature(signedInfo, keyInfo);
xmlSignature.sign(domSignContext);

Чтобы уточнить, мы генерируем XML-подпись для наших данных, присутствующих в файле “data.xml”. Между тем, есть несколько вещей, которые следует отметить в этом фрагменте кода:

  • Во-первых, XMLSignatureFactory является заводским классом для создания XML-подписей
  • XML-подпись требует SignedInfo объекта, над которым он вычисляет подпись
  • XML-подпись также нуждается в KeyInfo , которая инкапсулирует ключ подписи и сертификат
  • Наконец, XML-подпись подписывает документ, используя закрытый ключ, инкапсулированный как DOMSignContext

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

10. Безопасность За пределами Ядра Java

Как мы уже видели, платформа Java предоставляет множество необходимых функций для написания безопасных приложений. Однако иногда они являются довольно низкоуровневыми и непосредственно не применимы, например, к стандартному механизму безопасности в Интернете.

Например, при работе в нашей системе мы обычно не хотим читать полный RFC OAuth и реализовывать его самостоятельно . Нам часто нужны более быстрые и высокоуровневые способы обеспечения безопасности. Именно здесь на сцену выходят фреймворки приложений – они помогают нам достичь нашей цели с гораздо меньшим количеством шаблонного кода.

И, на платформе Java – обычно это означает Spring Security . Фреймворк является частью экосистемы Spring, но на самом деле его можно использовать вне приложения pure Spring.

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

Конечно, Spring Security подробно освещается в серии учебных пособий , а также в руководстве по курсу Learn Spring Security .

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

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

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

Подводя итог, это просто дает нам возможность заглянуть в функции безопасности Java. Следовательно, каждая из областей, обсуждаемых в этом учебном пособии, заслуживает дальнейшего изучения. Но, надеюсь, у нас будет достаточно проницательности, чтобы начать работу в этом направлении!