Security Top

The early-bird price of the new Learn Spring Security OAuth course packages will increase by $50 on Wednesday:

>> CHECK OUT THE COURSE
Java Top

The early-bird price of the new Learn Spring Security OAuth course packages will increase by $50 on Wednesday:

>> CHECK OUT THE COURSE

1. Overview

In this tutorial, we'll have a look into Java's built-in security infrastructure, which is disabled by default. Specifically, we'll examine its main components, extension points, and configurations.

2. SecurityManager in Action

It might be a surprise, but default SecurityManager settings disallow many standard operations:

System.setSecurityManager(new SecurityManager());
new URL("http://www.google.com").openConnection().connect();

Here, we programmatically enable security supervision with default settings and attempt to connect to google.com.

Then we get the following exception:

java.security.AccessControlException: access denied ("java.net.SocketPermission"
  "www.google.com:80" "connect,resolve")

There are numerous other use-cases in the standard library — for example, reading system properties, reading environment variables, opening a file, reflection, and changing the locale, to name a few.

3. Use-Case

This security infrastructure has been available since Java 1.0. This was a time where applets – Java applications embedded into the browser – were pretty common. Naturally, it was necessary to constrain their access to system resources.

Nowadays, applets are obsolete. However, security enforcement is still an actual concept when there is a situation in which third-party code executes in a protected environment.

For example, consider that we have a Tomcat instance where third-party clients may host their web applications. We don't want to allow them to execute operations like System.exit() because that would affect other applications and possibly the whole environment.

4. Design

4.1. SecurityManager

One of the main components in the built-in security infrastructure is java.lang SecurityManager. It has several checkXxx methods like checkConnect, which was authorizing our attempt to connect to Google in the test above. All of them delegates to the checkPermission(java.security.Permission) method.

4.2. Permission

java.security.Permission instances stand for authorization requests. Standard JDK classes create them for all potentially dangerous operations (like reading/writing a file, opening a socket, etc.) and give them over to SecurityManager for proper authorization.

4.3. Configuration

We define permissions in a special policy format. These permissions take the form of grant entries:

grant codeBase "file:${{java.ext.dirs}}/*" {
    permission java.security.AllPermission;
};

The codeBase rule above is optional. We can specify no field at all there or use signedBy (integrated with corresponding certificates in the keystore) or principal (java.security.Principal attached to the current thread via javax.security.auth.Subject). We can use any combination of those rules.

By default, the JVM loads the common system policy file located at <java.home>/lib/security/java.policy. If we've defined any user-local policy in  <user.home>/.java.policy, the JVM appends it to the system policy.

It's also possible to specify policy file via command line: –Djava.security.policy=/my/policy-file. That way we can append policies to the previously loaded system and user policies.

There is a special syntax for replacing all system and user policies (if any) – double equals sign: –Djava.security.policy==/my/policy-file

5. Example

Let's define a custom permission:

public class CustomPermission extends BasicPermission {
    public CustomPermission(String name) {
        super(name);
    }

    public CustomPermission(String name, String actions) {
        super(name, actions);
    }
}

and a shared service that should be protected:

public class Service {

    public static final String OPERATION = "my-operation";

    public void operation() {
        SecurityManager securityManager = System.getSecurityManager();
        if (securityManager != null) {
            securityManager.checkPermission(new CustomPermission(OPERATION));
        }
        System.out.println("Operation is executed");
    }
}

If we try to run it with a security manager enabled, an exception is thrown:

java.security.AccessControlException: access denied
  ("com.baeldung.security.manager.CustomPermission" "my-operation")

    at java.security.AccessControlContext.checkPermission(AccessControlContext.java:472)
    at java.security.AccessController.checkPermission(AccessController.java:884)
    at java.lang.SecurityManager.checkPermission(SecurityManager.java:549)
    at com.baeldung.security.manager.Service.operation(Service.java:10)

We can create our <user.home>/.java.policy file with the following content and try re-running the application:

grant codeBase "file:<our-code-source>" {
    permission com.baeldung.security.manager.CustomPermission "my-operation";
};

It works just fine now.

6. Conclusion

In this article, we checked how the built-in JDK security system is organized and how we can extend it. Even though the target use-case is relatively rare, it's good to be aware of it.

As usual, the complete source code for this article is available over on GitHub.

Security bottom

The early-bird price of the new Learn Spring Security OAuth course packages will increase by $50 on Wednesday:

>> CHECK OUT THE COURSE
Java bottom

The early-bird price of the new Learn Spring Security OAuth course packages will increase by $50 on Wednesday:

>> CHECK OUT THE COURSE
Comments are closed on this article!