Monday, October 12, 2009

NESS 2009: Remembering Java platform security

Java platform security is something we normally don't worry about when doing enterprise Java development. Originally designed as a sandbox for applets, Java platform security has evolved into a fine-grained access control architecture for all Java programs. Even so, it remains focused on protecting the host system from the Java application. The Java developer is more concerned about protecting the Java application from malicious users, or protecting users from each other.

So if Java platform security secures the host system, and we are concerned with securing our application, is the former useless? At the New England Software Symposium, I attended Ted Nugent's two presentations on Java platform security, and he argues that it is indeed useful. On the other hand, it is disabled by default, for ordinary Java applications as well as webapps running on Tomcat. This blog entry is based on Ted's presentation.



Why is it useful?

I consider Java platform security to be a firebreak. If an application's security is compromised, Java access control won't cure your ills, but it can limit the damage. Ted's example: suppose your webapp needs to serve files (say, reports) on the filesystem. A malicious client may manage to sneak a path like the following into your request:

../../../../etc/password

Thank you for your passwords.

Of course, that could be prevented using OS filesystem permissions that restrict the Tomcat user's access. But Tomcat will still need access to its own files. So mere OS level security won't protect you against requests for files like:

../../../tomcat-users.xml
../../jdbc.properties

Thank you for your user and database passwords.

Even if your webapp is totally flawless, you might be concerned about the other webapps with which you are sharing your Tomcat instance. Your Tomcat neighbor might have this in some JSP (the example from Tomcat's documentation):

<% System.exit(1); %>

Oops. When the ship goes down, everybody goes down with it.

Of course, if the affected apps are vulnerable in this way, you already have big headaches with or without security enabled. But again, Java platform security can limit the damage.

How it works

Java platform security contains the following parts:

  • Permission: identifies privileges -- access to the filesystem, network, properties etc -- that are restricted or granted. Specific permissions are identified by subclasses of Permission.
  • Policy: which identifies what permissions the code has. The dominant and default implementation is the text file based mechanism.
  • Enforcement: this is handled by AccessController. Legacy calls to SecurityManager are delegated to AccessController, but the latter can be used directly.

The policy mechanism is meant to be pluggable, but as there hasn't been a lot of innovation here you will almost certainly end up using the default implementation based on the java.policy text file. This policy file identifies the permissions for each code source, with an entry looking like (from Sun's documentation):

grant codeBase "http://java.sun.com/*", signedBy "Li" {
    permission java.io.FilePermission "/tmp/*", "read";
    permission java.io.SocketPermission "*", "connect";
};

The optional codeBase URL identifies the classes being granted permissions. This URL can identify specific classes, even in a jar (using the "jar:" prefix). The optional signedBy clause identifies the keystore alias(es) for signed code.

The code's permissions are checked when AccessController.checkPermission() is called. Java then walks up the call stack, conceptually speaking (there is some optimization), checking that all calling code in the stack has sufficient permissions for the operation. If not, an exception is thrown. Most of this is done by Java's system code for privileged operations like reading a file or accessing a network. However, you can also create your own subclasses of Permission and do your own permission checks.

Sometimes, trusted code does privileged operations on behalf of unprivileged code, such as loading a class. For this to be possible, the trusted code carries out the operation in a privileged block, using AccessController.doPrivileged(). This is prevents the permission check stack walk from proceeding higher, where it would certainly fail.

You can use the AccessControlContext class to capture the security context from one place to be evaluated in another context. You can later evaluate the permissions of this captured context in addition to the permissions of the actual security context. This is useful for deferred policy decisions, such as when worker threads need to evaluate the permissions of the calling thread.

What this means for you

If you are an application developer, I suspect Java platform security won't have much effect on your code. At the application level, we are more concerned with authentication, encryption and user-level security. Java's other security facilities for authentication and encryption and/or third party packages like Spring Security and Seam Security will be what you need to deal with explicitly. Java platform security is largely the concern of system library writers. All the security checks are already done in the system code, such as FileInputStream. Custom permissions are available should the need arise. Personally, I don't see the utility of custom permissions in my own code. I suggest the following practical things we can do to take advantage of Java platform security:

  • Turn it on.
  • Set up a suitable policy configuration for your application.
  • If you want to break up granted permissions among modules/authors, have the jars signed separately.

Securing Tomcat

Tomcat's documentation has a dedicated how-to on the security manager, so it is easy to get security going. Basically, you only need to edit the CATALINA_BASE/conf/catalina.policy file as you would the JRE's java.policy file. Then you add the -security flag when you start Tomcat:

catalina start -security

After that, you can watch your webapp go down in flames.

This is because, if you never ran your app with Java security enabled, it is not likely to work. You will need to grant your app sufficient permissions to work without giving it so much that security becomes useless. Ted Nugent suggested the following steps:

  • Set up a policy file that grants full permissions to your code. This is no worse than having security turned off. 
  • Use the -Djava.security.debug=flags switch to log security accesses. For example, the "access" flag will print all checkPermission calls. This will give you the list of permissions that you need to grant your application.
Conclusion

Security in general is important, and hopefully Java platform security is not the only thing you will think about. Nevertheless, it deserves a little attention. After all, Java pioneered the notion of a platform architected for security. It would be a shame if we did not take advantage of that facility.

No comments:

Post a Comment