Item 62: Turn on platform security



Item 62: Turn on platform security

Ever since JDK 1.0, one of Java's principal strengths has been its deep relationship with the idea of lacing security directly within the language and environment. The SecurityManager and its successor in JDK 1.2 and beyond, the AccessController, are a fundamental part of the runtime and environment—myriad actions within the Java environment trigger a security check, such as opening a file, attempting a socket connection to a server, and trying to read a system property.

To make things even better, starting with JDK 1.2, the security mechanism within the platform was extended and opened to allow non-Sun developers to write their own Permission classes (representing sensitive actions) and custom Policy classes (the class by which allowed permissions are decided and established). No more is Java code constrained to heavy-handed security environments like the applet sandbox. Now, thanks to the default file-based Policy implementation that ships with the JDK, developers have tremendous fine-grained control over exactly what sorts of things are allowed within the JVM—which makes it all the more shameful that not only do most J2EE developers ignore it, but so do most of the J2EE specifications and many J2EE container implementations.

For many enterprise Java developers, the Java Security Manager is something to be feared, loathed, and left alone as much as possible. Most developers first run into the SecurityManager in RMI code, and it shows up by suddenly throwing SecurityException instances everywhere at the worst possible time, usually when trying to put the application into production. Developers quickly learn how to set up a "let everything go by" policy file:






grant

{

  permission java.security.AllPermission;

}


This, of course, gets them past the immediate problem—if the security policy says to grant everything AllPermission, then nothing is ever actually blocked by the SecurityManager and we can now live happily ever after again, right?

It's not like we can really blame these poor Java coders, since anything related to the word "security" has huge piles of anxiety associated with it. When the chips are down, and the CEO is breathing down your neck to ship the application yesterday, wrestling with the Java policy file syntax is not what you want to be doing.

The problem, however, is that running Java code with this lenient permissions policy effectively gives an attacker a free pass should he or she somehow manage to lure your code into doing something evil. So much for the principle of least privilege (see Item 60). Because the Java Security Manager is no longer able to enforce restrictions, suddenly your servlet or JSP has complete access to anything within the JVM, and an attacker could conceivably start opening sockets, overwriting files on the local filesystem, and wreaking other malicious madness. The most frustrating part of all this is that this is precisely what the security manager was designed to prevent—if you let it.

For example, let's stop and look at the servlet container. From an enterprise perspective, each Web application deployed within a servlet container is intended to be an isolated, self-standing application that has no idea whether it is alone within the container or deployed alongside dozens (or hundreds) of other Web applications. From an Internet service provider's perspective, this is absolutely necessary, or my Web application could conceivably start making method calls on your servlets—this is generally considered a Bad Idea.

So what happens if I write a servlet that takes a relative filename as input and echoes that file back down the pipe as the response content? Intrinsically, nothing here seems amiss, but what happens if I feed it a relative filename (say, something like ../../../../etc/passwd) that goes right up the directory tree to the parent directory of all the installed Web applications and does a directory listing? Or goes "up and over" into another Web application's deployed directory and lists out its web.xml file? Or password files?

If the servlet container is running with an overly permissive Java Policy implementation, there's nothing the container can do to stop me; certainly, if I ask for the file through a servlet API, such as ServletContext's getResourceAsStream, the servlet container could intercept the request, realize it's outside my Web application, and reject it. Servlet containers might do this; it's not required by the specification that they do so. But what's to stop me if I create a File object with that relative path? The servlet container doesn't replace the standard Java runtime library, and since the Java Security Manager is rubber-stamping all requests, I've got a window into any other Web application installed on the system.

Turning the security manager on is a pretty simple thing: when executing the Java launcher (java.exe) to start up the J2EE container, set the java.security.manager system property by passing it via the -D option; you don't need to set it to anything, just add the JVM command-line parameter -Djava.security.manager to the script you use to launch the container. This will turn on the security manager, which in turn uses the java.policy file stored in the JRE's lib/security directory as the policy for the JVM. Assuming you're using independent JRE instances for the container (see Item 69), any changes required to the policy can be made directly within this file.

As a matter of fact, a number of changes are quite likely; the J2EE environment for the most part assumes that it will be running in a JVM with the security manager turned off, so turning security on will generate SecurityException instances pretty quickly; for example, J2EE assumes that the container is free to access system properties at any time, and since accessing a system property is a permission-constrained privilege, this permission needs to be granted to the container itself by explicitly granting it in the policy file. Similarly, if your code needs to reach across the network for any reason (e.g., to connect to a database), socket permissions will need to be granted, and so on.

Given that you're going to have to work out a security policy that describes precisely which permissions need to be granted to the container (as opposed to your component code), and that this is going to take a nontrivial amount of time, assuming your container vendor hasn't already provided you with one, why bother? We all have too much to do and not enough time to do it, so why waste time figuring out security policy? There has to be some legitimate payoff, right?

Assume for a moment that your Web application, running in the servlet container in your company's DMZ, connects to a database running on the server named dbserver.mycompany.com. You've established a security policy that allows for connections to that machine and nowhere else (permission java.net.SocketPermission "dbserver.mycompany. com", "connect"). Now an attacker manages to successfully hijack your servlet and wants to execute some arbitrary Java code; despite the fact that he's got control of the thread, when he tries to open a connection to anywhere other than the database server, he's going to run afoul of the security manager, and a SecurityException will be thrown. The same will happen if he tries to open a new socket to accept connections on some random high-numbered port, or if he tries to write to the filesystem anywhere outside of your Web application's deployed directory, and so on.

By itself, turning on the security manager within your J2EE container may seem like more work than it's worth, particularly if the vendor implementation doesn't have a security policy definition tuned for its own use. (If your vendor doesn't, consider switching to another vendor product, or put some pressure on the vendor to provide one.) When coupled with using JAAS, however, as described in Item 63, the benefits of the Java platform security model become much clearer.