The Security Manager Is Permanently Disabled
As of JDK 24, the Security Manager is permanently disabled.
The following is a summary of the changes made to the JDK as a result of the Security Manager being permanently disabled:
- You can't enable the Security Manager at startup, nor can you install a custom Security Manager during run time. See Enabling the Security Manager Is an Error.
- Methods that are part of the Security Manager API have been degraded to
either return
null
orfalse
, pass through the caller's request, or unconditionally throw a SecurityException or UnsupportedOperationException. See Changes to the Security Manager API. - Many other APIs are changed, in particular, those that throw a SecurityException. See Other Changes to APIs.
- System properties and security properties specific to the Security Manager are ignored. See Affected System and Security Properties.
- The system policy file,
$JAVA_HOME/conf/security/java.policy
, is removed. - Changes not directly related to API behavior, system or security properties, or changes to the JDK files are listed in Other Changes.
The Security Manager API will be removed in a future JDK release.
See Migrating Away From the Security Manager for tips on verifying if your application requires a Security Manager and migrating away from it.
See JEP 486: Permanently Disable the Security Manager for more information, including an explanation of why the Security Manager is permanently disabled and will be removed in a future release.
Enabling the Security Manager Is an Error
You can't enable the Security Manager nor install a custom Security Manager at startup, for example:
java -Djava.security.manager -jar app.jar
java -Djava.security.manager="" -jar app.jar
java -Djava.security.manager=allow -jar app.jar
java -Djava.security.manager=default -jar app.jar
java -Djava.security.manager=com.example.CustomSecurityManager -jar app.jar
Attempting to do so causes the JVM to report the error and exit, for example:
Error occurred during initialization of VM
java.lang.Error: A command line option has attempted to allow or enable the
Security Manager. Enabling a Security Manager is not supported.
at java.lang.System.initPhase3(java.base@24/System.java:2067)
You can't suppress this error message nor change it to a warning.
You also can't install a Security Manager at run time by calling System.setSecurityManager(SecurityManager). Attempting to do so causes the JVM to throw an UnsupportedOperationException with the following message:
Setting a Security Manager is not supported
Changes to the Security Manager API
The Security Manager API consists of the following:
- Methods in the java.lang.SecurityManager class
- Methods in the AccessController, AccessControlContext, Policy, and ProtectionDomain classes of the java.security package
- The getSecurityManager and setSecurityManager methods in the java.lang.System class
As a result of the Security Manager being permanently disabled, these
methods, as appropriate, return null
or false
,
pass through the caller's request, or unconditionally throw a SecurityException or UnsupportedOperationException.
The removal of the Security Manager API is planned for a future release.
The following table lists the changes to the Security Manager API in detail:
Table 1-4 APIs Affected by the Security Manager Being Permanently Disabled
Class | Affected APIs |
---|---|
java.lang.SecurityManager |
The check* methods always throw a SecurityException. The getSecurityContext
method returns an AccessControlContext that grants no permissions.
In particular, the checkPermission method throws an AccessControlException and the
getDomainCombiner method
returns |
java.lang.System |
The setSecurityManager method always throws an UnsupportedOperationException. The getSecurityManager method always returns
|
java.lang.ClassLoader |
The default domain assigned to classes by the
defineClass(String, byte[], int,
int) method is the same as before but it's not
granted any permissions. In particular, the ProtectionDomain.getPermissions()
method always returns |
java.security.AccessController |
The doPrivileged methods (6 variants) and the doPrivilegedWithCombiner methods (4 variants) execute the action immediately and behave as if a Security Manager has not been enabled. The checkPermission method always throws an AccessControlException. The getContext method returns an AccessControlContext that grants no permissions. In particular, the checkPermission method throws an AccessControlException, and the getDomainCombiner method returns null. |
java.security.AccessControlContext |
The checkPermission method always throws a SecurityException. |
java.security.Permission |
The checkGuard method always throws a SecurityException. |
java.security.ProtectionDomain |
A ProtectionDomain won't be granted any permissions from the current policy because the current policy is always a Policy object that grants no permissions. |
java.security.Policy |
The setPolicy method always throws an UnsupportedOperationException. The getPolicy method
returns a Policy object that
grants no permissions. In particular, the getParameters method returns
|
javax.security.auth.Subject |
The getSubject method always throws an UnsupportedOperationException. The doAs methods (2 variants) and the doAsPrivileged methods (2 variants) launch the action and bind the subject to the period of execution. |
java.rmi.RMISecurityManager |
All of the changes to the java.lang.SecurityManager class also apply to this subclass. |
java.rmi.server.RMIClassLoader |
The getSecurityContext method always returns null. |
Other Changes to APIs
- Many methods and constructors were specified to throw a SecurityException if a Security Manager was enabled and appropriate permissions had not been granted. These methods no longer throw a SecurityException. They operate as if a Security Manager has not been enabled.
- The following methods do nothing: java.lang.Thread.checkAccess(), java.lang.ThreadGroup.checkAccess(), and java.util.logging.LogManager.checkAccess().
Affected System and Security Properties
- The following system properties are not supported:
java.security.policy
,jdk.security.filePermCompat
,sun.security.policy.utf8
,sun.security.policy.numcaches
, andsun.net.maxDatagramSockets
. - The
access
andpolicy
options of thejava.security.debug
system property are removed. - The following security properties are not supported:
policy.provider
,policy.url.n
,policy.ignoreIdentityScope
,package.access
, andpackage.definition
.
Other Changes
The RMI Remote Code Downloading mechanism is removed. The java.rmi.server.RMIClassLoader class has a service provider interface (SPI) that can be configured through a system property. See RMIClassLoader.getDefaultProviderInstance() for information about the default SPI implementation. The default provider previously supported a feature called Remote Code Downloading which allowed an RMI client to load classes from a codebase specified by an RMI server, and the other way around. Remote Code Downloading was enabled only when a Security Manager was enabled. With the Security Manager being permanently disabled, the Remote Code Downloading mechanism is removed. As a workaround, you can perform class loading from the codebase in your own SPI implementation.
Migrating Away From the Security Manager
See How to Determine if an Application Enables the Security Manager to verify whether your application is using a Security Manager.
If you determine that your application enables the Security Manager, consider replacing it with technologies outside the JDK such as the following:
- Containers, which are a lightweight and portable form of virtualization technology, allow applications and their dependencies to be packaged and run consistently across different environments.
- Hypervisors, which are also called Virtual Machine Monitors (VMMs), enable multiple operating systems to share the hardware resources offered by a single host computer.
- Operating system mechanisms such as the macOS App Sandbox or the Linux secure computing (seccomp) feature.
Like the Security Manager, these technologies can restrict how applications use local and remote resources. For example, they can prevent code from accessing the network to exfiltrate data.
A small number of libraries were designed to use the Security Manager if it is enabled. Some of these may also use advanced parts of the Security Manager API to implement a custom execution environment. See Advice to Maintainers of Libraries That Support the Security Manager.
A small number of applications use the Security Manager not to enforce a security policy but, rather, as a means to intercept calls to the Java Platform API. If your application requires interception, you can use alternative techniques such as source code modification, static code analysis and rewriting, and agent-based dynamic code rewriting at class load time. See An Agent that Blocks Code From Calling System.exit(int) for an example of an agent that uses dynamic code rewriting.
How to Determine if an Application Enables the Security Manager
Try the following techniques to determine if an application enables the Security Manager:
- Check scripts or documentation to see if the application is launched with the Security Manager allowed or enabled through command-line options, or requires policy files to be installed and configured.
- Run the application on a JDK release from 17 to 23 and look for messages on the console warning that the Security Manager will be deprecated and will be removed in a future release. See the Issue warnings section in JEP 411: Deprecate the Security Manager for Removal.
- Run the application on a JDK release from 17 to 23 with the
command-line option
-Djava.security.manager=disallow
. If the application installs a custom Security Manager through the System.setSecurityManager(SecurityManager) method, the JVM will throw an UnsupportedOperationException. - Use the
jdeprscan
tool from a JDK release from 17 to 23 to scan for uses of deprecated Security Manager APIs such as System.setSecurityManager(SecurityManager) or java.security.Policy.setPolicy(Policy).
Advice to Maintainers of Libraries That Support the Security Manager
A small number of libraries were designed to use the Security Manager if it is enabled. These libraries typically employ two idioms:
-
Calling System.getSecurityManager() to check if a Security Manager is enabled and, if so, calling one of the SecurityManager.checkPermission methods to check if an operation should be granted or denied:
SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkPermission(...); }
-
Calling one of the AccessController.doPrivileged methods to run code with different permissions from the calling code:
SomeReturnValue v = AccessController.doPrivileged(() -> { // ... return theResult; });
In JDK 24 and later, where a Security Manager is never enabled, the System.getSecurityManager()method and the AccessController.doPrivileged methods behave as they did in prior JDK releases when a Security Manager was not enabled:
- System.getSecurityManager() returns
null
- The six AccessController.doPrivileged methods run the given action immediately
Accordingly, the small number of libraries that call these methods will run on JDK 24 and later without change. However, we strongly recommend that new releases of these libraries do not call these methods, which will be removed in a future release.
A very small number of libraries use advanced parts of the Security Manager API to implement a custom execution environment. For example, a library might call AccessController.checkPermission(Permission) to enforce its own permission model or call Policy.setPolicy(Policy) to make custom Security Managers treat certain resources as off-limits.
- The AccessController.checkPermission(Permission) method always throws AccessControlException
- The Policy.setPolicy(Policy) method always throws UnsupportedOperationException
- The SecurityManager.check* methods always throw SecurityException
An Agent that Blocks Code From Calling System.exit(int)
An agent is a Java program that can alter the code of an application while the application is running. Agents achieve this by transforming the bytecodes of methods when classes are loaded, or by redefining classes after they have been loaded. See the java.lang.instrument package for more information about agents.
The example Example 1-1 is an agent that blocks code from calling System.exit(int). The agent declares a premain
method that is run by the JVM before the main
method of the
application. This method registers a transformer that transforms class files
as they are loaded from the class path or module path. The transformer rewrites
every call to System.exit(int) into throw
new RuntimeException("System.exit not allowed")
.
The transformer reads and writes bytecodes in class files using the Class-File API.
Example 1-1 BlockSystemExitAgent.java
import java.lang.classfile.*;
import java.lang.classfile.instruction.InvokeInstruction;
import java.lang.constant.ClassDesc;
import java.lang.constant.MethodTypeDesc;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Predicate;
public class BlockSystemExitAgent {
/*
* Before the application starts, register a transformer of class files.
*/
public static void premain(String agentArgs, Instrumentation inst) {
var transformer = new ClassFileTransformer() {
@Override
public byte[] transform(ClassLoader loader,
String className,
Class<?> classBeingRedefined,
ProtectionDomain protectionDomain,
byte[] classBytes) {
if (loader != null && loader != ClassLoader.getPlatformClassLoader()) {
return blockSystemExit(classBytes);
} else {
return null;
}
}
};
inst.addTransformer(transformer, true);
}
/*
* Rewrite every invokestatic of System.exit(int) to an athrow of RuntimeException.
*/
private static byte[] blockSystemExit(byte[] classBytes) {
var modified = new AtomicBoolean();
ClassFile cf = ClassFile.of(ClassFile.DebugElementsOption.DROP_DEBUG);
ClassModel classModel = cf.parse(classBytes);
Predicate<MethodModel> invokesSystemExit =
methodModel -> methodModel.code()
.map(codeModel ->
codeModel.elementStream()
.anyMatch(BlockSystemExitAgent::isInvocationOfSystemExit))
.orElse(false);
CodeTransform rewriteSystemExit =
(codeBuilder, codeElement) -> {
if (isInvocationOfSystemExit(codeElement)) {
var runtimeException = ClassDesc.of("java.lang.RuntimeException");
codeBuilder.new_(runtimeException)
.dup()
.ldc("System.exit not allowed")
.invokespecial(runtimeException,
"<init>",
MethodTypeDesc.ofDescriptor("(Ljava/lang/String;)V"),
false)
.athrow();
modified.set(true);
} else {
codeBuilder.with(codeElement);
}
};
ClassTransform ct = ClassTransform.transformingMethodBodies(invokesSystemExit, rewriteSystemExit);
byte[] newClassBytes = cf.transformClass(classModel, ct);
if (modified.get()) {
return newClassBytes;
} else {
return null;
}
}
private static boolean isInvocationOfSystemExit(CodeElement codeElement) {
return codeElement instanceof InvokeInstruction i
&& i.opcode() == Opcode.INVOKESTATIC
&& "java/lang/System".equals(i.owner().asInternalName())
&& "exit".equals(i.name().stringValue())
&& "(I)V".equals(i.type().stringValue());
}
}
To use BlockSystemExitAgent
, you must package it in a
JAR file and specifiy it with the -javaagent
option when starting
an application.
Follow these steps to compile, package, and use
BlockSystemExitAgent
:
-
Compile
BlockSystemExitAgent
in the directoryagentclasses
:$ javac -d agentclasses BlockSystemExitAgent.java
-
Create a JAR file manifest in
agent.mf
:$ cat > agent.mf << EOF Premain-Class: BlockSystemExitAgent Can-Retransform-Classes: true EOF
-
Create the agent JAR file. Note that there's a period after
-C agentclasses
:$ jar --create --file=BlockSystemExitAgent.jar --manifest=agent.mf -C agentclasses .
-
Run your application (which is packaged in
app.jar
in this example) with the agent JAR file:$ java -javaagent:BlockSystemExitAgent.jar -jar app.jar