Команда Spring АйО перевела статью про ужесточение контроля за динамической загрузкой агентов, ограничения доступа к опасным методам работы с памятью и JNI в новых версиях JDK.
Java продолжает развиваться, делая упор на стабильность и устойчивость. Новые версии JDK отражают этот тренд: ограничивается динамическая загрузка агентов, прекращается поддержка опасных методов доступа к памяти, усиливается контроль над использованием JNI. В JDK 22 появилось Foreign Function and Memory (FFM) API, которое упрощает вызов внешнего кода и обеспечивает безопасный доступ к памяти без управления со стороны JVM. Эти изменения поддерживают концепцию "целостности по умолчанию" и формируют более предсказуемую и надёжную экосистему Java. В этой статье мы подробно разберём, как эти обновления укрепляют платформу и делают е безопаснее.
Целостность в платформе Java
В программном обеспечении термин “целостность” (integrity) означает гарантию того, что конструкции, из которых мы создаем программы, являются завершенными и надежными. Это означает, что спецификации Java Platform полностью покрывают предметную область, а ее реализации строго соответствуют этим спецификациям. Такая фундаментальная целостность позволяет вам создавать логику своего приложения с уверенностью в том, что вы можете положиться на конструкции платформы Java.
В программировании "целостность" (integrity) — это гарантия, что конструкции, используемые для создания программ, завершенные и надежные. В Java это означает, что спецификации платформы полностью охватывают предметную область, а их реализации строго соответствуют этим спецификациям. Такая заложенная в основу платформы целостность позволяет разработчикам уверенно создавать приложения, опираясь на проверенные механизмы платформы.
JEP "Целостность по умолчанию" (Integrity by Default) акцентирует внимание на защите кода и данных от нежелательного вмешательства. Хотя инкапсуляция — ключевой инструмент для обеспечения целостности, платформа Java всё ещё содержит небезопасные API, которые могут эту целостность подорвать. Это способно негативно повлиять на корректность, простоту в поддержке, безопасность, производительность и масштабируемость приложений или библиотек:
Instrumentation API позволяет агентам модифицировать байткод любого метода в любом классе.
Метод AccessibleObject::setAccessible(boolean) позволяет игнорировать границы инкапсуляции при рефлексии. Он предназначен для поддержки сериализации и десериализации объектов, но открывает доступ к вызову приватных методов, чтению и изменению приватных полей, а также записи в final поля. Это делает возможным вмешательство в структуру любого класса, нарушая принципы инкапсуляции.
Класс sun.misc.Unsafe включает методы, которые могут получать доступ к private методам и полям и записывать в final поля, игнорируя границы инкапсуляции.
Java Native Interface (JNI) позволяет нативному коду взаимодействовать с объектами Java. Однако он также предоставляет доступ к private методам и полям, а также возможность изменять final поля, обходя ограничения инкапсуляции.
Чтобы предотвратить некорректное использование небезопасных API, JEP предполагает ограничение прямого доступа к ним, чтобы библиотеки, фреймворки и инструменты не могли обращаться к ним по умолчанию. Кроме того, текущие JEP должны постепенно решить проблемы, связанные с использованием таких API, предлагая альтернативы для миграции и позволяя обходить вводимые по умолчанию ограничения, если это действительно необходимо.
Постепенное ограничение доступа к небезопасным API и альтернативы
Организуйтесь, чтобы запретить динамическую загрузку агентов
В качестве развития темы целостности JDK 21, JEP 451 сфокусировался на рисках, связанных с динамической загрузкой агентов в работающую JVM. Агенты, появившиеся в JDK 5, позволяют оснащать классы инструментами, такими как профайлеры, для мониторинга приложений. Например, для отладки удаленного приложения вы, возможно, используете опцию -agentlib:jdwp, позволяющую включить встроенный в JVM агент при запуске. Внутри Java использует Attach API, который с соответствующими привилегиями на уровне ОС позволяет инструментам подключаться к работающей JVM.
Некоторые библиотеки используют Attach API, чтобы незаметно подключаться к JVM, загружать агенты динамически и получить практически неограниченные возможности в плане изменения кода на лету. Однако такие действия несут риски для целостности приложения. Начиная с JDK 21, JVM предупреждает о динамической загрузке агентов, чтобы информировать о возможных угрозах и подготовить к будущим релизам, где такие действия могут быть запрещены по умолчанию. Это изменение не затрагивает большинство инструментов, которые не требуют динамической загрузки агентов.
