JEP 411: Deprecate the Security Manager for Removal

OwnerSean Mullan
TypeFeature
ScopeSE
StatusCompleted
Release17
Componentsecurity-libs / java.security
Discussionsecurity dash dev at openjdk dot java dot net
EffortM
DurationM
Reviewed byAlan Bateman, Alex Buckley, Brian Goetz
Endorsed byBrian Goetz
Created2021/04/05 14:19
Updated2021/07/30 19:51
Issue8264713

Summary

Deprecate the Security Manager for removal in a future release. The Security Manager dates from Java 1.0. It has not been the primary means of securing client-side Java code for many years, and it has rarely been used to secure server-side code. To move Java forward, we intend to deprecate the Security Manager for removal in concert with the legacy Applet API (JEP 398).

Goals

Non-Goals

It is not a goal to provide a replacement for the Security Manager. Future JEPs or enhancements may define new APIs or mechanisms for specific use cases, depending upon demand.

Motivation

The Java Platform emphasizes security. The integrity of data is protected by the Java language and VM's built-in memory safety: Variables are initialized before use, array bounds are checked, and memory deallocation is completely automatic. Meanwhile, the confidentiality of data is protected by the Java class libraries' trusted implementations of modern cryptographic algorithms and protocols such as SHA-3, EdDSA, and TLS 1.3. Security is a dynamic science, thus we continuously update the Java Platform to address new vulnerabilities and to reflect new industry postures, for example by deprecating weak cryptographic protocols.

One long-time element of security is the Security Manager, which dates from Java 1.0. In the era of Java applets downloaded by web browsers, the Security Manager protected the integrity of users' machines and the confidentiality of their data by running applets in a sandbox, which denied access to resources such as the file system or the network. The small size of the Java class libraries — only eight java.* packages in Java 1.0 — made it feasible for code in, e.g., java.io to consult with the Security Manager before performing any operation. The Security Manager drew a bright line between untrusted code (applets from a remote machine) and trusted code (classes on the local machine): It would approve all operations involving resource access for trusted code but reject them for untrusted code.

As interest in Java grew, we introduced signed applets to allow the Security Manager to place trust in remote code, thereby allowing applets to access the same resources as local code run via java on the command line. Simultaneously, the Java class libraries were expanding rapidly — Java 1.1 introduced JavaBeans, JDBC, Reflection, RMI, and Serialization — which meant that trusted code gained access to significant new resources such as database connections, RMI servers, and reflective objects. Allowing all trusted code to access all resources was undesirable, so in Java 1.2 we redesigned the Security Manager to focus on applying the principle of least privilege: All code would be treated as untrusted by default, subject to sandbox-style controls that prevented access to resources, and users would place trust in specific codebases by granting them specific permissions to access specific resources. In theory, an application JAR on the class path might be more limited in how it uses the JDK than an applet from the Internet. Limiting permissions was seen as a way to constrain the impact of any vulnerabilities that might exist in a body of code — in effect, a defense-in-depth mechanism.

The Security Manager, then, had ambitions to protect against two kinds of threat: Malicious intent, especially in remote code, and accidental vulnerabilities, especially in local code.

The threat of malicious intent by remote code has receded because the Java Platform no longer supports applets. The Applet API was deprecated in Java 9 in 2017, then deprecated for removal in Java 17 in 2021 with the intent to remove it in a future release. The closed-source browser plugin that ran applets was removed from Oracle's JDK 11 in 2018 along with the closed-source Java Web Start technology. Accordingly, many of the risks that the Security Manager protects against are no longer significant. Furthermore, the Security Manager cannot protect against many risks that now are significant. The Security Manager cannot address 19 of the 25 most dangerous issues identified by industry leaders in 2020, so issues such as XML external entity reference (XXE) injection and improper input validation have required direct countermeasures in the Java class libraries. (For example, JAXP can protect against XXE attacks and XML entity expansion, while serialization filtering can prevent malicious data from being deserialized before it can do any damage.) The Security Manager is also incapable of preventing malicious behavior based on speculative-execution vulnerabilities.

The Security Manager's lack of efficacy against malicious intent is unfortunate because the Security Manager is, of necessity, woven into the fabric of the Java class libraries. As such, it is an ongoing maintenance burden. All new features and APIs must be evaluated to ensure that they behave correctly when the Security Manager is enabled. Access control based on the least-privilege principle may have been feasible in the class libraries of Java 1.0, but the rapid growth of java.* and javax.* packages led to dozens of permissions and hundreds of permission checks throughout the JDK. This is a significant surface area to keep secure, especially since permissions can interact in surprising ways. Some permissions, e.g., allow application or library code to perform a series of safe operations whose overall effect is sufficiently unsafe that it would require a more powerful permission if granted directly.

The threat of accidental vulnerabilities in local code is almost impossible to address with the Security Manager. Many of the claims that the Security Manager is widely used to secure local code do not stand up to scrutiny; it is used far less in production than many people assume. There are many reasons for its lack of use:

In the quarter-century since the Security Manager was introduced, adoption has been low. Only a handful of applications ship with policy files that constrain their own operations (e.g., ElasticSearch). Similarly, only a handful of frameworks ship with policy files (e.g., Tomcat), and developers building applications with those frameworks still face the practically insurmountable challenge of figuring out the permissions needed by their own code and by the libraries they use. Some frameworks (e.g., NetBeans) eschew policy files and instead implement a custom Security Manager in order to prevent plugins from calling System::exit or to gain insight into code's behavior, such as whether it opens files and network connections — use cases which we think are better served by other means.

In summary, there is no significant interest in developing modern Java applications with the Security Manager. Making access-control decisions based on permissions is unwieldy, slow, and falling out of favor across the industry; .NET, e.g., no longer supports it. Security is better achieved by providing integrity at lower levels of the Java Platform — by, for example, strengthening module boundaries (JEP 403) to prevent access to JDK implementation details, and hardening the implementation itself — and by isolating the entire Java runtime from sensitive resources via out-of-process mechanisms such as containers and hypervisors. To move the Java Platform forward, we will deprecate the legacy Security Manager technology for removal from the JDK. We plan to deprecate and attenuate the capabilities of the Security Manager over a number of releases, simultaneously creating alternative APIs for such tasks as blocking System::exit and other use cases considered important enough to have replacements.

Description

In Java 17, we will:

In Java 18, we will prevent a Java application or library from dynamically installing a Security Manager unless the end user has explicitly opted to allow it. Historically, a Java application or library was always allowed to dynamically install a Security Manager, but since Java 12, the end user has been able to prevent it by setting the system property java.security.manager to disallow on the command line (java -Djava.security.manager=disallow ...) -- this causes System::setSecurityManager to throw an UnsupportedOperationException. Starting in Java 18, the default value of java.security.manager will be disallow if not otherwise set via java -D.... As a result, applications and libraries that call System::setSecurityManager may fail due to an unexpected UnsupportedOperationException. In order for System::setSecurityManager to work as before, the end user will have to set java.security.manager to allow on the command line (java -Djava.security.manager=allow ...).

In Java 18 and later, we will degrade other Security Manager APIs so that they remain in place but with limited or no functionality. For example, we may revise AccessController::doPrivileged simply to run the given action, or revise System::getSecurityManager always to return null. This will allow libraries that support the Security Manager and were compiled against previous Java releases to continue to work without change or even recompilation. We expect to remove the APIs once the compatibility risk of doing so declines to an acceptable level.

In Java 18 and later, we may alter the Java SE API definition so that operations which previously performed permission checks no longer perform them, or perform fewer checks when a Security Manager is enabled. As a result, @throws SecurityException will appear on fewer methods in the API specification.

Deprecate APIs for removal

The Security Manager consists of the class java.lang.SecurityManager and a number of closely related APIs in the java.lang and java.security packages. We will terminally deprecate the following eight classes and two methods, by annotating them with @Deprecated(forRemoval=true):

We will also terminally deprecate the following two classes and eight methods which depend strongly on the Security Manager:

We will not deprecate some classes in the java.security package that are related to the Security Manager, for various reasons:

We will not deprecate the javax.security.auth.Subject::doAs method since it can be used to transport a Subject across API boundaries by attaching it to the thread's AccessControlContext, serving a purpose similar to a ThreadLocal. The credentials of the Subject can then be obtained by an underlying authentication mechanism (e.g., a Kerberos implementation of GSSAPI) by calling Subject::getSubject. These credentials can be used for authentication or authorization purposes and do not require the Security Manager to be enabled. However, Subject::doAs depends on APIs tightly related to the Security Manager, such as AccessControlContext and DomainCombiner. Thus, we plan to create a new API that does not depend on the Security Manager APIs; subsequently we will then deprecate the Subject::doAs API for removal.

We will not deprecate any tools. (We removed the policytool GUI for editing policy files in JDK 10.)

Issue warnings

We will make the following changes to ensure that developers and users are aware that the Security Manager is deprecated for removal.

Future Work

This JEP is about deprecating the Security Manager for removal in the future; it does not propose to remove the Security Manager now. Thus there is time to consider use cases where the Security Manager is useful today and where developing replacements of, or alternatives to, some of its functionality may be justified. Here is a list of potential enhancements and current work in progress:

Alternatives

Each of these alternatives requires retaining the Security Manager in something close to its current form. After decades of maintaining the Security Manager but seeing very little usage, we are no longer willing to bear this ongoing and expensive burden.

Testing

We will add new tests to verify that warnings are issued when the Security Manager is enabled on the command line or dynamically installed at run time.

Risks and Assumptions