The Java Security Manager: Why and How?
You've got questions, we've got answers: Learn more about Java security manager, including how code compiles, if it runs, and what it displays. Then we'll probe the why and how.
Join the DZone community and get the full member experience.
Join For FreeGenerally, security concerns are boring for developers. I hope this article is entertaining enough for you to read it until the end since it tackles a very serious issue on the JVM.
Quiz
Last year, at Joker conference, my colleague Volker Simonis showed a snippet that looked like the following:
public class StrangeReflectionExample {
public Character aCharacter;
public static void main(String... args) throws Exception {
StrangeReflectionExample instance = new StrangeReflectionExample();
Field field = StrangeReflectionExample.class.getField("aCharacter");
Field type = Field.class.getDeclaredField("type");
type.setAccessible(true);
type.set(field, String.class);
field.set(instance, 'A');
System.out.println(instance.aCharacter);
}
}
Now a couple of questions:
- Does this code compile?
- If yes, does it run?
- If yes, what does it display?
Answers below (dots to let you think before checking them).
.
..
…
….
…..
……
…….
……..
………
……….
………..
…………
………….
…………..
……………
…………….
……………..
This code compiles just fine. In fact, it uses the so-called reflection API (located in the java.lang.reflect
package) which is fully part of the JDK.
Executing this code leads to the following exception:
Exception in thread "main" java.lang.IllegalArgumentException: Can not set java.lang.String field ch.frankel.blog.securitymanager.StrangeReflectionExample.aCharacter to java.lang.Character
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:167)
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:171)
at sun.reflect.UnsafeObjectFieldAccessorImpl.set(UnsafeObjectFieldAccessorImpl.java:81)
at java.lang.reflect.Field.set(Field.java:764)
at ch.frankel.blog.securitymanager.StrangeReflectionExample.main(StrangeReflectionExample.java:15)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
So, despite the fact that we defined the type of the aCharacter
attribute as a Character
at development time, the reflection API is able to change its type to String
at runtime! Hence, trying to set it to 'A'
fails.
Avoiding Nasty Surprises with the Security Manager
Reflection is not the only risky operation one might want to keep in check on the JVM. Reading a file or writing one also belong to the set of potentially dangerous operations. Fortunately, the JVM has a system to restrict those operations. Unfortunately, it’s not set by default.
In order to activate the SecurityManager, just launch the JVM with the java.security.manager
system property i.e. java -Djava.security.manager
. At this point, the JVM will use the default JRE policy. It’s configured in the file located at%JAVA_HOME%/lib/security/java.policy
(for Java 8). Here’s a sample of this file:
grant codeBase "file:${{java.ext.dirs}}/*" {
permission java.security.AllPermission;
};
grant {
permission java.lang.RuntimePermission "stopThread";
permission java.net.SocketPermission "localhost:0", "listen";
permission java.util.PropertyPermission "java.version", "read";
permission java.util.PropertyPermission "java.vendor", "read";
...
}
The first section – grant codeBase
, is about which code can be executed; the second – grant
, is about specific permissions.
Regarding the initial problem regarding reflection mentioned above, the second part is the most relevant. One can read the source of the AccessibleObject.setAccessible()
method:
SecurityManager sm = System.getSecurityManager();
if (sm != null) sm.checkPermission(ACCESS_PERMISSION);
setAccessible0(this, flag);
Every sensitive method to a Java API has the same check through the Security Manager. You can verify that for yourself in the following code:
Thread.stop()
Socket.bind()
System.getProperty()
- etc.
Using an Alternate java.policy File
Using the JRE’s policy file is not convenient when one uses the same JRE for different applications. Given the current micro-service trend, this might not be the case. However, with automated provisioning, it might be more convenient to always provision the same JRE over and over and let each application provides its own specific policy file.
To add another policy file in addition to the default JRE’s, thus adding more permissions, launch the JVM with:java -Djava.security.manager -Djava.security.policy=/path/to/other.policy
To replace the default policy file with your own, launch the JVM with:java -Djava.security.manager -Djava.security.policy==/path/to/other.policy
Note the double equal sign.
Configuring Your Own Policy File
Security configuration can be either based on a:
- Black list
- In a black list scenario, everything is allowed but exceptions can be configured to disallow some operations.
- White list
- On the opposite, in a white list scenario, only operations that are explicitly configured are allowed. By default, all operations are disallowed.
If you want to create your own policy file, it’s suggested you start with a blank one and then launch your app. As soon, as you get a security exception, add the necessary permission is the policy. Repeat until you have all necessary permissions. Following this process will let you have only the minimal set of permissions to run the application, thus implementing the least privilege security principle.
Note that if you’re using a container or a server, you’ll probably require a lot of those permissions, but this is the price to pay to secure your JVM against abuses.
Conclusion
I never checked policy files in production, but since I never had any complain, I assume the JVM’s policy was never secured. This is a very serious problem! I hope this article will raise awareness regarding that lack of hardening – especially since with the latest JVM, you can create and compile Java code on the fly, leading to even more threats.
To go further:
Opinions expressed by DZone contributors are their own.
Comments