JEP 411: Deprecate the Security Manager for Removal
Перевод Security Manager в статус Deprecated for Removal (устаревший, будет удалён)
| Ответственный | Sean Mullan |
| Тип | Feature |
| Область | SE |
| Статус | Closed / Delivered |
| Выпуск | 17 |
| Компонент | security-libs / java.security |
| Обсуждение | security dash dev at openjdk dot java dot net |
| Трудоёмкость | M |
| Длительность | M |
| Связан с | JEP 486: Permanently Disable the Security Manager |
| Рецензенты | Alan Bateman, Alex Buckley, Brian Goetz |
| Одобрен | Brian Goetz |
| Создан | 2021/04/05 14:19 |
| Обновлён | 2025/05/13 14:45 |
| Задача | 8264713 |
Аннотация
Перевести Security Manager в статус Deprecated for Removal, чтобы удалить его в одном из будущих выпусков. Security Manager существует со времён Java 1.0. Уже много лет он не является основным средством защиты клиентского кода на Java, для защиты серверного кода он применялся редко, а его поддержка обходится дорого. Чтобы развивать платформу Java дальше, мы переведём Security Manager в статус Deprecated for Removal вместе с устаревшим Applet API (JEP 398).
Цели
-
Подготовить разработчиков к удалению Security Manager в одной из будущих версий Java.
-
Предупреждать пользователей, если их Java-приложения зависят от Security Manager.
-
Оценить, нужны ли новые API или механизмы для отдельных узких сценариев, в которых применялся Security Manager, например для блокировки
System::exit.
Что не является целью
Предоставить замену Security Manager не является целью. В будущих JEP или улучшениях могут быть определены новые API или механизмы для конкретных сценариев в зависимости от спроса.
Мотивация
Платформа Java уделяет особое внимание безопасности. Целостность данных защищена встроенной безопасностью памяти языка Java и виртуальной машины: переменные инициализируются до использования, границы массивов проверяются, а освобождение памяти полностью автоматическое. В то же время конфиденциальность данных защищают надёжные реализации современных криптографических алгоритмов и протоколов, таких как SHA-3, EdDSA и TLS 1.3, в библиотеках классов Java. Безопасность — динамичная область знаний, поэтому мы постоянно обновляем платформу Java, чтобы устранять новые уязвимости и отражать новые позиции отрасли, например переводя слабые криптографические протоколы в статус Deprecated (устаревший).
Один из давних элементов безопасности — Security Manager, существующий со времён Java 1.0. В эпоху Java-апплетов, которые загружались веб-браузерами, Security Manager защищал целостность компьютеров пользователей и конфиденциальность их данных, запуская апплеты в песочнице, которая запрещала доступ к таким ресурсам, как файловая система или сеть. Небольшой размер библиотек классов Java — в Java 1.0 было всего восемь пакетов java.* — позволял коду, например, в java.io обращаться к Security Manager перед выполнением любой операции. Security Manager проводил чёткую границу между недоверенным кодом (апплетами с удалённой машины) и доверенным кодом (классами на локальной машине): он разрешал доверенному коду все операции с доступом к ресурсам, но отклонял их для недоверенного кода.
По мере роста интереса к Java мы ввели подписанные апплеты, чтобы Security Manager мог доверять удалённому коду и тем самым позволять апплетам обращаться к тем же ресурсам, что и локальный код, запущенный через java из командной строки. Одновременно быстро расширялись библиотеки классов Java — в Java 1.1 появились JavaBeans, JDBC, Reflection, RMI и Serialization, — а значит, доверенный код получил доступ к важным новым ресурсам, таким как соединения с базами данных, серверы RMI и рефлективные объекты. Разрешать всему доверенному коду доступ ко всем ресурсам было нежелательно, поэтому в Java 1.2 мы переработали Security Manager, сосредоточившись на применении принципа наименьших привилегий: по умолчанию весь код считался бы недоверенным и подчинялся бы ограничениям в стиле песочницы, запрещающим доступ к ресурсам, а пользователи доверяли бы определённым кодовым базам, выдавая им определённые разрешения на доступ к определённым ресурсам. Теоретически JAR-файл приложения в пути классов мог быть ограничен в использовании JDK сильнее, чем апплет из интернета. Ограничение разрешений рассматривалось как способ сдержать последствия любых уязвимостей, которые могли существовать в коде, — по сути, как механизм эшелонированной защиты.
Таким образом, Security Manager был призван защищать от угроз двух видов: злого умысла, особенно в удалённом коде, и случайных уязвимостей, особенно в локальном коде.
Угроза злого умысла со стороны удалённого кода отступила, потому что платформа Java больше не поддерживает апплеты. Applet API получил статус Deprecated в Java 9 в 2017 году, затем статус Deprecated for Removal в Java 17 в 2021 году с намерением удалить его в одном из будущих выпусков. Плагин браузера с закрытым исходным кодом, запускавший апплеты, был удалён из Oracle JDK 11 в 2018 году вместе с технологией Java Web Start с закрытым исходным кодом. Поэтому многие риски, от которых защищает Security Manager, больше не значимы. Более того, Security Manager не может защитить от многих рисков, которые значимы сейчас. Security Manager не способен справиться с 19 из 25 самых опасных проблем, выявленных лидерами отрасли в 2020 году, поэтому против таких проблем, как внедрение ссылок на внешние сущности XML (XXE) и некорректная проверка входных данных, потребовались прямые контрмеры в библиотеках классов Java. (Например, JAXP может защищать от атак XXE и раскрытия сущностей XML, а фильтрация сериализации может не допустить десериализации вредоносных данных до того, как они нанесут какой-либо ущерб.) Кроме того, Security Manager не способен предотвратить вредоносное поведение, основанное на уязвимостях спекулятивного выполнения.
Неэффективность Security Manager против злого умысла тем досаднее, что Security Manager по необходимости вплетён в саму ткань библиотек классов Java. Поэтому его поддержка — постоянное бремя. Все новые возможности и API нужно оценивать, чтобы убедиться, что они работают правильно при включённом Security Manager. Контроль доступа на основе принципа наименьших привилегий, возможно, был осуществим в библиотеках классов Java 1.0, но быстрый рост пакетов java.* и javax.* привёл к десяткам разрешений и сотням проверок разрешений по всему JDK. Это значительная поверхность, которую нужно поддерживать защищённой, тем более что разрешения могут взаимодействовать неожиданным образом. Например, некоторые разрешения позволяют коду приложения или библиотеки выполнить серию безопасных операций, общий эффект которых настолько небезопасен, что при прямой выдаче для него потребовалось бы более сильное разрешение.
Угрозу случайных уязвимостей в локальном коде почти невозможно устранить с помощью Security Manager. Многие утверждения о том, что Security Manager широко используется для защиты локального кода, не выдерживают проверки: в промышленной эксплуатации он используется гораздо реже, чем полагают многие. У того, что он мало используется, много причин:
-
Хрупкая модель разрешений — разработчик приложения, который хочет получить пользу от Security Manager, должен тщательно выдать все разрешения, которые требуются приложению для всех выполняемых им операций. Частичной защиты, при которой контролю доступа подлежат лишь некоторые ресурсы, добиться невозможно. Например, допустим, разработчик опасается неправомерного доступа к данным и поэтому хочет разрешить чтение файлов только из определённого каталога. Выдать разрешение на чтение файлов недостаточно, потому что приложение почти наверняка использует в библиотеках классов Java и другие операции, помимо чтения файлов (например, запись файлов), а эти операции Security Manager отклонит, так как у кода не будет соответствующего разрешения. Выдать нужные разрешения сможет только разработчик, который тщательно документирует, как его код взаимодействует с критичными для безопасности операциями библиотек классов Java. Для разработчиков это необычный порядок работы. (Security Manager не допускает отрицательных разрешений, которыми можно было бы выразить «разрешить все операции, кроме чтения файлов».)
-
Сложная модель программирования — Security Manager разрешает критичную для безопасности операцию, проверяя разрешения всего выполняющегося кода, который привёл к этой операции. Из-за этого трудно писать библиотеки, работающие с Security Manager: разработчику библиотеки недостаточно задокументировать разрешения, нужные коду его библиотеки. Разработчик приложения, использующий библиотеку, также должен выдать те же разрешения коду своего приложения в дополнение к разрешениям, уже выданным этому коду. Это нарушает принцип наименьших привилегий, так как коду приложения разрешения библиотеки для собственных операций могут быть не нужны. Разработчик библиотеки может сдержать это вирусное разрастание разрешений, аккуратно используя API
java.security.AccessController, чтобы Security Manager учитывал только разрешения библиотеки, но сложность этого и других рекомендаций по безопасному программированию далеко выходит за рамки интересов большинства разработчиков. Путь наименьшего сопротивления для разработчиков приложений часто состоит в том, чтобы выдатьAllPermissionвсем нужным JAR-файлам, но это опять же противоречит принципу наименьших привилегий. -
Низкая производительность — в основе Security Manager лежит сложный алгоритм контроля доступа, который часто приводит к неприемлемому снижению производительности. Поэтому для JVM, запускаемых из командной строки, Security Manager всегда был по умолчанию отключён. Это ещё больше снижает готовность разработчиков вкладывать силы в то, чтобы библиотеки и приложения работали с Security Manager. Дополнительным препятствием служит отсутствие инструментов, помогающих выводить и проверять разрешения.
За четверть века, прошедшую с появления Security Manager, он получил слабое распространение. Лишь немногие приложения поставляются с файлами политик, ограничивающими их собственные операции (например, ElasticSearch). Точно так же лишь немногие фреймворки поставляются с файлами политик (например, Tomcat), и разработчики, создающие приложения на этих фреймворках, по-прежнему сталкиваются с практически непреодолимой задачей: выяснить, какие разрешения нужны их собственному коду и используемым библиотекам. Некоторые фреймворки (например, NetBeans) обходятся без файлов политик и вместо этого реализуют собственный Security Manager, чтобы не давать плагинам вызывать System::exit или чтобы получать сведения о поведении кода, например открывает ли он файлы и сетевые соединения, — по нашему мнению, для таких сценариев лучше подходят другие средства.
Итак, серьёзного интереса к разработке современных Java-приложений с Security Manager нет. Принятие решений о доступе на основе разрешений громоздко, медленно и теряет популярность во всей отрасли; например, .NET больше это не поддерживает. Безопасности лучше добиваться, обеспечивая целостность на более низких уровнях платформы Java — например, укрепляя границы модулей (JEP 403), чтобы закрыть доступ к деталям реализации JDK, и повышая защищённость самой реализации, — а также изолируя всю среду выполнения Java от чувствительных ресурсов с помощью внепроцессных механизмов, таких как контейнеры и гипервизоры. Чтобы развивать платформу Java дальше, мы переведём устаревшую технологию Security Manager в статус Deprecated for Removal, чтобы затем удалить её из JDK. Мы планируем на протяжении нескольких выпусков переводить Security Manager в статус Deprecated и сокращать его возможности, одновременно создавая альтернативные API для таких задач, как блокировка System::exit, и для других сценариев, которые сочтены достаточно важными, чтобы получить замену.
Описание
В Java 17 мы:
-
переведём большинство классов и методов, связанных с Security Manager, в статус Deprecated for Removal;
-
будем выводить предупреждение при запуске, если Security Manager включён в командной строке;
-
будем выводить предупреждение во время выполнения, если Java-приложение или библиотека устанавливает Security Manager динамически.
В Java 18 мы запретим Java-приложениям и библиотекам динамически устанавливать Security Manager, если конечный пользователь явно этого не разрешил. Исторически Java-приложению или библиотеке всегда разрешалось динамически устанавливать Security Manager, но начиная с Java 12 конечный пользователь может это запретить: для этого в командной строке системному свойству java.security.manager задаётся значение disallow (java -Djava.security.manager=disallow ...), и тогда System::setSecurityManager выбрасывает UnsupportedOperationException. Начиная с Java 18 значением java.security.manager по умолчанию будет disallow, если оно не задано иначе через java -D.... В результате приложения и библиотеки, вызывающие System::setSecurityManager, могут завершаться с ошибкой из-за неожиданного UnsupportedOperationException. Чтобы System::setSecurityManager работал как прежде, конечному пользователю придётся задать в командной строке для java.security.manager значение allow (java -Djava.security.manager=allow ...).
В выпусках Feature Release (функциональный выпуск) после Java 18 мы ограничим остальные API Security Manager: они останутся на месте, но их функциональность будет ограничена или будет отсутствовать совсем. Например, мы можем изменить AccessController::doPrivileged так, чтобы он просто выполнял переданное действие, или изменить System::getSecurityManager так, чтобы он всегда возвращал null. Так библиотеки, которые поддерживают Security Manager и были скомпилированы для предыдущих выпусков Java, продолжат работать без изменений и даже без перекомпиляции. Мы рассчитываем удалить эти API, когда риск нарушения совместимости при их удалении снизится до приемлемого уровня.
В выпусках Feature Release после Java 18 мы можем изменить определение API Java SE так, чтобы операции, которые раньше выполняли проверки разрешений, при включённом Security Manager больше их не выполняли или выполняли меньше проверок. В результате @throws SecurityException будет встречаться в спецификации API у меньшего числа методов.
Перевод API в статус Deprecated for Removal
Security Manager состоит из класса java.lang.SecurityManager и ряда тесно связанных с ним API в пакетах java.lang и java.security. Мы окончательно переведём в статус Deprecated for Removal следующие восемь классов и два метода, пометив их аннотацией @Deprecated(forRemoval=true):
-
java.lang.SecurityManager— основной API Security Manager. -
java.lang.System::{setSecurityManager, getSecurityManager}— методы для установки и получения Security Manager. -
java.security.{Policy, PolicySpi, Policy.Parameters}— основные API политики (policy), с помощью которых определяется, предоставлено ли коду, работающему под Security Manager, разрешение на выполнение определённых привилегированных операций. -
java.security.{AccessController, AccessControlContext, AccessControlException, DomainCombiner}— основные API контроллера доступа, то есть реализации по умолчанию, которой Security Manager делегирует проверки разрешений. Без Security Manager эти API бесполезны, поскольку некоторые операции не работают без реализации политики и поддержки контекста контроля доступа в VM.
Мы также окончательно переведём в статус Deprecated for Removal следующие два класса и восемь методов, которые сильно зависят от Security Manager:
-
java.lang.Thread::checkAccess,java.lang.ThreadGroup::checkAccessиjava.util.logging.LogManager::checkAccess— эти три метода нетипичны: они позволяют обычному Java-коду проверить, доверили бы ему выполнение определённых операций, не выполняя их на самом деле. Без Security Manager они не имеют смысла. -
java.util.concurrent.Executors::{privilegedCallable, privilegedCallableUsingCurrentClassLoader, privilegedThreadFactory}— эти служебные методы полезны только при включённом Security Manager. -
java.rmi.RMISecurityManager— класс Security Manager для RMI. Этот класс устарел и был помечен как Deprecated в Java 8. -
javax.security.auth.SubjectDomainCombinerиjavax.security.auth.Subject::{doAsPrivileged, getSubject}— API авторизации на основе пользователей, которые зависят от API Security Manager, таких какAccessControlContextиDomainCombiner. Мы планируем предоставить API на замену дляSubject::getSubject, поскольку он часто используется в сценариях, не требующих Security Manager, и продолжить поддерживать сценарии с участиемSubject::doAs(см. ниже).
Некоторые классы пакета java.security, связанные с Security Manager, мы по разным причинам не будем помечать как Deprecated:
-
SecureClassLoader— суперклассjava.net.URLClassLoader. Кроме того, начиная с Java 9SecureClassLoaderиграет важную роль в реализации загрузчика классов приложения и загрузчика классов платформы. -
CodeSource— хотяCodeSourceчаще всего связывают с предоставлением разрешений в зависимости от расположения кода, он не привязан напрямую к Security Manager и имеет самостоятельную ценность как способ определить источник фрагмента кода и, при необходимости, того, кто его подписал. -
ProtectionDomain— отProtectionDomainзависит ряд важных API, напримерClassLoader::defineClassиClass::getProtectionDomain.ProtectionDomainтакже имеет ценность независимо от Security Manager, поскольку содержитCodeSourceкласса. -
Permissionи подклассы — отPermissionзависят другие важные классы, напримерProtectionDomain. Однако многие подклассыPermissionотносятся к сценариям, которые, скорее всего, перестанут быть актуальными после удаления Security Manager. Сопровождающие этих подклассов могут отдельно пометить их как Deprecated и удалить, оценив риск нарушения совместимости. -
PermissionCollectionиPermissions— классы, которые хранят коллекции объектовPermissionи не зависят напрямую от Security Manager. -
PrivilegedAction,PrivilegedExceptionActionиPrivilegedActionException— эти API не зависят напрямую от Security Manager и используются APIjavax.security.authдля аутентификации и авторизации (см. ниже). -
SecurityException— исключение времени выполнения, которое выбрасывают Java API при неудачной проверке разрешения. Позднее мы можем перевести этот API в статус Deprecated for Removal, но пока последствия этого были бы слишком серьёзными.
Мы не будем помечать как Deprecated метод javax.security.auth.Subject::doAs, поскольку с его помощью можно передавать Subject через границы API, прикрепляя его к AccessControlContext потока, — примерно так же, как ThreadLocal. Затем нижележащий механизм аутентификации (например, реализация GSSAPI для Kerberos) может получить учётные данные Subject, вызвав Subject::getSubject. Эти учётные данные можно использовать для аутентификации или авторизации, и для этого не требуется включать Security Manager. Однако Subject::doAs зависит от API, тесно связанных с Security Manager, таких как AccessControlContext и DomainCombiner. Поэтому мы планируем создать новый API, не зависящий от API Security Manager; после этого мы переведём API Subject::doAs в статус Deprecated for Removal.
Мы не будем помечать как Deprecated никакие инструменты. (Графический инструмент policytool для редактирования файлов политик мы удалили в JDK 10.)
Вывод предупреждений
Мы внесём следующие изменения, чтобы разработчики и пользователи знали, что Security Manager переведён в статус Deprecated for Removal.
-
Если при запуске включён Security Manager по умолчанию или пользовательский Security Manager:
java -Djava.security.manager MyApp java -Djava.security.manager="" MyApp java -Djava.security.manager=default MyApp java -Djava.security.manager=com.foo.bar.Server MyAppто при запуске выводится следующее предупреждение:
WARNING: A command line option has enabled the Security Manager WARNING: The Security Manager is deprecated and will be removed in a future releaseВ отличие от предупреждений об устаревании во время компиляции, это предупреждение нельзя подавить.
(Четыре приведённых выше вызова
java -D...задают системному свойствуjava.security.managerсоответственно пустую строку, пустую строку, строкуdefaultи имя класса пользовательского Security Manager. Именно так поддерживалось включение Security Manager при запуске в выпусках Java до Java 12. В Java 12 добавлена поддержка строкallowиdisallow, показанных далее.) -
Если Security Manager не включён при запуске, но может быть установлен динамически во время выполнения:
java MyApp java -Djava.security.manager=allow MyAppто при запуске предупреждение не выводится. Вместо этого предупреждение выводится во время выполнения при вызове
System::setSecurityManager, как показано ниже:WARNING: A terminally deprecated method in java.lang.System has been called WARNING: System::setSecurityManager has been called by com.foo.bar.Server (file:/tmp/foobarserver/thing.jar) WARNING: Please consider reporting this to the maintainers of com.foo.bar.Server WARNING: System::setSecurityManager will be removed in a future releaseЭто предупреждение выводится один раз для каждого вызывающего кода и, в отличие от предупреждений об устаревании во время компиляции, не может быть подавлено.
-
Если Security Manager не включён при запуске, а системное свойство
java.security.managerимеет значениеdisallow:java -Djava.security.manager=disallow MyAppто предупреждение не выводится ни при запуске, ни во время выполнения при попытке динамически установить Security Manager вызовом
System::setSecurityManager. Однако каждый вызовSystem::setSecurityManagerвыбрасываетUnsupportedOperationExceptionсо следующим сообщением:The Security Manager is deprecated and will be removed in a future releaseВ Java 18
disallowстанет значениемjava.security.managerпо умолчанию. Тогда командная строкаjava MyAppбудет действовать так же, какjava -Djava.security.manager=disallow MyAppв Java 17.
Дальнейшая работа
Этот JEP посвящён переводу Security Manager в статус Deprecated for Removal с удалением в будущем; удалять Security Manager сейчас он не предлагает. Поэтому есть время рассмотреть сценарии, в которых Security Manager полезен сегодня и в которых может быть оправдана разработка замен или альтернатив части его функциональности. Ниже приведён список возможных улучшений и работ, которые ведутся сейчас:
-
Защита доступа к нативному коду — приложения, работающие с Security Manager, могут с помощью разрешений запрещать загрузку нативного кода, чтобы к нему нельзя было обратиться через Java Native Interface (JNI). Планируемая замена JNI — Foreign Function & Memory API (JEP 412), который предоставляет Java API для взаимодействия с кодом и данными за пределами среды выполнения Java; он будет защищать доступ к нативному коду, не опираясь на Security Manager.
-
Мониторинг доступа к ресурсам — приложения, работающие с Security Manager, иногда используют его для мониторинга или журналирования таких операций, как доступ к файлам и сети, не обязательно ограничивая эти операции. Для мониторинга подобной активности, возможно, есть более удачные способы, например JDK Flight Recorder. Мы оценим возможность добавить новые события JFR для сетевых операций, файловой системы и создания процессов, чтобы повысить безопасность приложений и дать представление о том, какие API платформы выполняют такие операции.
-
Блокировка
System::exit— некоторые IDE и фреймворки используют пользовательский Security Manager, чтобы запретить приложениям вызывать этот метод. Для этого сценария может пригодиться новый API. -
Защита десериализации — приложения, которые работают с Security Manager и десериализуют данные, уязвимы для атак, если разрешения предоставлены неправильно (см., например, Java Secure Coding Guideline 8-5). В качестве альтернативы Serialization Filters (JEP 290) позволяют проверять входящие данные на раннем этапе, а Context-Specific Deserialization Filters (JEP 415) сделают эту проверку более гибкой и детальной.
-
Защита обработки XML — как описано в разделе «Мотивация», в JAXP есть режим более безопасной обработки XML. Этот режим включается явно, но также включён по умолчанию, когда приложения работают с Security Manager. Мы изучим возможность включить этот режим по умолчанию независимо от того, включён ли Security Manager. (В XML Signature есть аналогичный режим безопасной проверки, который включается явно, но по умолчанию включён, если включён Security Manager. Начиная с Java 17 этот режим включён по умолчанию независимо от того, включён ли Security Manager.)
Альтернативы
-
Сохранить API Security Manager для расширения пользовательскими реализациями Security Manager, которым нужно перехватывать, журналировать и запрещать доступ к ресурсам — убрать поддержку файлов политик, но сохранить механизм проверки разрешений, встроенный в библиотеки классов Java.
Этот вариант вынуждает разработчиков изучать принципы и лучшие практики архитектуры Security Manager, включая сложную науку проверки разрешений, ради гораздо более простой цели — мониторинга ресурсов. Кроме того, он, скорее всего, поставил бы вопрос, стоит ли сохранять в JDK все точки проверки разрешений с вызовами Security Manager: в
System::exit— да, вSystem::getProperty— возможно, нет. Мы считаем, что вместо этого стоит заняться улучшением средств с похожими возможностями, например JDK Flight Recorder. -
Усовершенствовать Security Manager — дорабатывать Security Manager для новых сценариев или для устранения его многочисленных недостатков непрактично. Включать Security Manager по умолчанию, запускать код с
AllPermissionи журналировать все проверки разрешений, чтобы побудить разработчиков относиться к нему серьёзнее, неразумно. -
Оставить Security Manager как есть — продолжать поддерживать его в текущем виде, не вкладывая дальнейших усилий в его улучшение.
Каждая из этих альтернатив требует сохранить Security Manager примерно в его нынешнем виде. Мы десятилетиями поддерживали Security Manager, но видели, что используют его очень мало, и больше не готовы нести это постоянное и дорогостоящее бремя.
Тестирование
Мы добавим новые тесты, проверяющие, что предупреждения выдаются, когда Security Manager включается в командной строке или динамически устанавливается во время выполнения.
Риски и допущения
-
Security Manager входит в платформу Java начиная с JDK 1.0, поэтому перевод его в разряд устаревших и последующее удаление могут затронуть некоторые приложения. Однако в выпуске, для которого предназначен этот JEP, вся функциональность Security Manager будет сохранена. Приложения могут ещё некоторое время полагаться на поддерживаемые JDK, пока они переходят на более новые API и механизмы.
-
Jakarta EE предъявляет к Security Manager ряд требований. Мы предполагаем, что эти требования будут ослаблены или отменены, чтобы соответствующие им приложения могли работать в будущих выпусках Java после того, как возможности Security Manager будут сокращены, а затем он будет удалён.