В декабре прошлого года Log4Shell сократил ночи многих людей в мире JVM. Хуже того, используя аналогию с землетрясением, после первоначального землетрясения возникло множество афтершоков.
Я сразу установил связь между Log4Shell и Security Manager. Сначала я не хотел об этом писать. Но ко мне поступили просьбы, и я не мог обойти это стороной.
![](https://habrastorage.org/getpro/habr/upload_files/781/176/7e2/7811767e290f8d478e857066e9470e70.png)
Насколько я помню команда Oracle отказалась от Security Manager в Java 17. Один из аргументов, на котором основывалось это решение, заключается в том, что он изначально был разработан для защиты апплетов.
Апплеты загружались из Интернета, поэтому их нужно было считать ненадежным кодом. Следовательно, нам пришлось запускать их в песочнице.
Хотя они никогда этого не говорили, из этого утверждения следует неявное следствие: поскольку апплеты теперь устарели, мы запускаем только доверенный код. Следовательно, мы можем отказаться от диспетчера безопасности.
Это просто неправильно, и я объясню в этом посте почему.
Предположение о том, что коду, работающему внутри вашей инфраструктуры, можно доверять, опасно как локально, так и в облаке. Позвольте мне перечислить некоторые аргументы в поддержку этого утверждения.
Библиотекам нельзя доверять
Мудрые разработчики не изобретают велосипед: они используют существующие библиотеки и/или фреймворки.
Очевидно, что с точки зрения безопасности это означает, что пользователи такого стороннего кода должны тщательно его проверять. Надо искать недостатки: и баги, и уязвимости.
За два десятилетия работы в отрасли я никогда не видел такого аудита.
Можно было бы подискутировать о пользе кастомного кода. К сожалению, это ничего не решает. Кастомный код страдает от тех же проблем, ошибок и уязвимостей. Хуже того, ему не уделяется такого же внимания, как стандартным библиотекам, поэтому исследователи не могут тратить свое время на поиск этих проблем - это ничего не стоит.
Сборкам нельзя доверять
Представьте, что у вас есть все ресурсы, необходимые для аудита кода — время, деньги и навыки.
Представьте себе далее, что аудит не выявил ничего подозрительного. Наконец, представьте, что заключение аудита на 100% достоверно.
Проблема в том, что ничто не гарантирует, что JAR является результатом сборки из исходного кода, даже если сборка общедоступна. Злоумышленник может заменить подлинный JAR другим.
Идентификации нельзя доверять
Поставщик может подписать JAR, чтобы гарантировать его подлинность. Подпись основана на асимметричной криптографии:
Поставщик подписывает JAR своим закрытым ключом.
Он генерирует открытый ключ с закрытым ключом
Можно прочитать подпись с помощью открытого ключа и проверить, что провайдер подписал JAR.
Следовательно, любой может проверить, что JAR исходит от определенного поставщика.
JDK предоставляет jarsigner
- инструмент для подписи JAR-файлов. К сожалению, большинство библиотек не используют его. В качестве примера я проверил следующие зависимости:
org.slf4j:slf4j-api:1.7.32
com.fasterxml.jackson.core:jackson-core:2.13.0
org.mockito:mockito-core:4.1.0
org.junit.jupiter:junit-jupiter-api:5.8.2
org.apache.commons:commons-collections4:4.4
org.eclipse.collections:eclipse-collections:10.4.0
com.google.protobuf:protobuf-java:3.18.0
com.itextpdf:itextpdf:5.5.13.2
com.zaxxer:HikariCP:5.0.0
com.vladmihalcea.flexy-pool:flexy-pool-core:2.2.3
org.springframework:spring-beans:5.3.13
jakarta.platform:jakarta.jakartaee-api:9.1.0
Среди двенадцати файлов JAR, указанных выше, только один подписан с расширением jarsigner
. Если вам интересно, это Eclipse Collections.
Однако для противодействия атакам на цепочку поставок репозитории артефактов стали требоваться подписанные артефакты. Например, Sonatype требует подписи для каждого загруженного файла, т. е. POM, JAR, исходного JAR, JAR JavaDocs и т. д.
Можно проверить подпись с помощью Maven:
mvn org.simplify4u.plugins:pgpverify-maven-plugin:show -Dartifact=com.zaxxer:HikariCP:5.0.0
Он выводит следующее:
Artifact:
groupId: com.zaxxer
artifactId: HikariCP
type: jar
version: 5.0.0
PGP signature:
version: 4
algorithm: SHA256withRSA
keyId: 0x4CC08E7F47C3EC76
create date: Wed Jul 14 04:49:52 CEST 2021
status: valid
PGP key:
version: 4
algorithm: RSA (Encrypt or Sign)
bits: 2048
fingerprint: 0xF3A90E6B10E809F851AB4FC54CC08E7F47C3EC76
create date: Wed Sep 18 02:51:23 CEST 2013
uids: [Brett Wooldridge (Sonatype) <brett.wooldridge@gmail.com>]
Однако все это не имеет большого значения. Подписание не подтверждает идентичность поставщика. Оно сообщает, что JAR-файл подписан с помощью закрытого ключа, ссылающегося на адрес электронной почты.
Ничто не мешает злоумышленнику создать еще один закрытый ключ с тем же адресом электронной почты или похожим.
Функциям нельзя доверять
На данный момент я думаю, нарисованная картина выглядит довольно мрачной. Но все еще хуже. Ничто из вышеперечисленного не объясняет уязвимость Log4J. Основная причина заключается в том, что он предоставляет функции, которые большинству разработчиков не нужны и не используются.
Я не хочу вдаваться в подробности, так как это уже объяснялось во многих местах. Достаточно сказать, что Log4J обеспечивает lookup. Lookup (поиск и подстановка) обеспечивает интеграцию с другой системой, которая дает возможность обогатить лог, выйдя за рамки простых сообщений. Например, Spring Boot lookup позволяет получить свойства Spring Boot. Лог имеет смысл обогатить, например, файлами spring.application.name
.
Во всех используемых поисковых запросах некоторые кажутся подозрительными. Например, переменные среды, системные свойства или даже JNDI. Именно последнее является основной причиной уязвимости Log4J.
Этот тип скрытых функций не специфичен для Log4J. Я случайно знаю, что внутри драйвера базы данных H2 есть приложение для администрирования с графическим интерфейсом на основе Swing. Я узнал об этом случайно.
Примечание переводчика. Консоль базы данных H2 имеет ВЕБ интерфейс.
Проблема в том, что разработчики используют библиотеку для своих основных нужд, например, для ведения журналов. Если остановиться на этом, то никогда не узнаешь всех возможностей библиотеки.
Следовательно, удивительно, что библиотека делает что-то, чего от нее не предполагалось, например, читает из удаленного дерева ресурсов JNDI.
JVM нельзя доверять
Я признаю, что название раздела вводит в заблуждение, но я не смог найти более подходящее. Это продолжение предыдущего раздела, на этот раз применительно к JVM.
JVM предоставляет множество функций, из которых вы используете несколько. Самая вопиющая проблема — это Attach API. Этот API, доступный начиная с Java 1.6, позволяет JVM обновлять байт-код, уже загруженный в другую JVM. Да, вы правильно прочитали: вы можете изменить байт-код запущенного приложения. Хуже того, если вы перезапустите JVM, код снова загрузится, не оставив следов.
Это отличная функция, если вы хотите быстро внести исправление в рабочей среде.
Тем не менее:
Большинство людей не используют его
Большинство людей не знают об этом
Функция должна быть явно отключена. Она включена по умолчанию.
Могу я предложить, чтобы вы первым делом завтра проверили свою инфраструктуру и отключили ее?
Менеджеру по безопасности можно доверять
Я надеюсь, что на этом этапе вы понимаете проблему. Большинству кода, который вы используете, нельзя доверять.
Хуже того, я рассматриваю только обычные приложения: программное обеспечение, построенное на архитектуре плагинов, по определению запускает ненадежный код.
Security Manager был компонентом JVM, который позволял вам определять белый список того, что может делать приложение, независимо от кода приложения.
Это решало все вышеперечисленные проблемы: вы могли запускать любой код, но позволяли ему делать только ограниченное количество вещей.
Менеджер безопасности имел несколько недостатков, главный из которых заключался в том, что настраивать разрешения было скучно. Однако существуют инструменты для создания файла политики. Поскольку они автоматизированы, вам необходимо тщательно просмотреть обнаруженные разрешения. Легче прочитать примерно 500 строк конфигурации, чем 10 или 100 тысяч строк кода.
Поскольку многие не знали об инструментах, мало кто использовал Security Manager. Но когда он был, он был очень полезен.
Чтобы подтвердить мое утверждение, вы можете прочитать этот пост или перейти к выводу: хотя Elasticsearch встраивает уязвимую версию Log4J, она не восприимчива к Log4Shell!
Вывод
Безопасность — это нефункциональное требование. NFR не дают никаких конкурентных преимуществ и стоят денег. Короче говоря, они отвлекают бюджет от бизнес требований к /dev/null
. По крайней мере, так это видят в большинстве бизнес подразделений.
Я думаю, что мы должны рассматривать вопросы безопасности через призму оценки рисков. Требуется сначала перечислить все возможные риски.
Я боюсь, что маркировка Security Manager как устаревший (Deprecated) только добавило несколько рисков, связаных с запуском ненадежного кода.
Обратите внимание, что дебаты об устаревании Security Manager не были публичными. С тех пор как я выступил против устаревания, я подвергся публичным нападкам, вплоть до простого издевательства. Другие голоса, поддержавшие меня, подверглись аналогичному обращению. Я не ожидаю, что реакция на этот пост будет другой.
Однако я должен был рассказать членам сообщества, что произошло и что мы потеряли.
Спасибо Peter Firmstone и Geertjan Wielenga за помощь в рецензировании этого поста.
Что еще почитать: