Monday, 25 August 2014

A Per-Thread Java Security Manager

This post explains how you can customise the Java SecurityManager to so that it can be programatically enabled and disabled on a per-thread basis.

The security manager mechanism in Java is great (Java 1.7 bugs notwithstanding): it gives you a simple, reliable way to restrict what different parts of your code can do. Most of the time, the standard SecurityManager implementation along with a carefully-defined security policy will meet your security needs, which is lovely. However, there are times when the standard SecurityManager implementation doesn't cut it. This post address a couple of those cases.

One of the main problems with the SecurityManager is that it introduces a performance overhead. This is because every time a restricted method is called, the SecurityManager has to check the call stack to make sure that the necessary permissions have been granted to the code in every frame. A lot of the time this isn't an issue, but if your application is performance-sensitive and makes a lot of calls to restricted functions, these checks can add up.

It's also not uncommon to find applications that only need to place restrictions on code running in specific threads: most of the threads in the application will only ever run trusted code, but one or two threads will be used to call some un-trusted libraries. The standard SecurityManager, when installed, applies to all code running in the JVM, even if the majority of the threads in the VM won't run un-trusted code. Even if you define your security policy such that your trusted code is granted all the permissions it needs, a lot of unnecessary checks will be made by the SecurityManager.

A nice solution to this is to define a custom SecurityManager that can be enabled and disabled programatically on a per-thread basis. This can be achieved pretty easily using ThreadLocal variables to store the enabled/disabled state of the SecurityManager on each thread:

package demo;

import java.security.Permission;

public class SelectiveSecurityManager extends SecurityManager {

  private static final ToggleSecurityManagerPermission TOGGLE_PERMISSION = new ToggleSecurityManagerPermission();

  ThreadLocal enabledFlag = null;

  public SelectiveSecurityManager(final boolean enabledByDefault) {

    enabledFlag = new ThreadLocal() {

      @Override
      protected Boolean initialValue() {
        return enabledByDefault;
      }

      @Override
      public void set(Boolean value) {
        SecurityManager securityManager = System.getSecurityManager();
        if (securityManager != null) {
          securityManager.checkPermission(TOGGLE_PERMISSION);
        }
        super.set(value);
      }
    };
  }

  @Override
  public void checkPermission(Permission permission) {
    if (shouldCheck(permission)) {
      super.checkPermission(permission);
    }
  }

  @Override
  public void checkPermission(Permission permission, Object context) {
    if (shouldCheck(permission)) {
      super.checkPermission(permission, context);
    }
  }

  private boolean shouldCheck(Permission permission) {
    return isEnabled() || permission instanceof ToggleSecurityManagerPermission;
  }

  public void enable() {
    enabledFlag.set(true);
  }

  public void disable() {
    enabledFlag.set(false);
  }

  public boolean isEnabled() {
    return enabledFlag.get();
  }

}

The custom SecurityManager simply sub-classes the standard SecurityManager and overrides the checkPermisson methods so that a ThreadLocal boolean is tested to see if the check is necessary before it's carried out. The ThredLocal boolean can be set programatically via an enable/disable methods, and we make sure that only authorised code can enable/disable the security manager by checking a special ToggleSecurityManagerPermission in the set method of the ThredLocal. The ToggleSecurityManagerPermission class is very simple, and looks like this:

package demo;

import java.security.Permission;

public class ToggleSecurityManagerPermission extends Permission {

  private static final long serialVersionUID = 4812713037565136922L;
  private static final String NAME = "ToggleSecurityManagerPermission";

  public ToggleSecurityManagerPermission() {
    super(NAME);
  }

  @Override
  public boolean implies(Permission permission) {
    return this.equals(permission);
  }

  @Override
  public boolean equals(Object obj) {
    if (obj instanceof ToggleSecurityManagerPermission) {
      return true;
    }
    return false;
  }

  @Override
  public int hashCode() {
    return NAME.hashCode();
  }

  @Override
  public String getActions() {
    return "";
  }

}

That's pretty much all there is to it. You need to define a security policy that will ensure that any trusted code on the stack for threads where the SecurityManager will be enabled has the necessary permissions, and that trusted code has permission to enabled and disable the SecurityManager, but that's it. For a working example of the above with some simple tests, see the Github repository https://github.com/alphaloop/selective-security-manager.

Saturday, 2 August 2014

Sandboxing Python Scripts in a Java Application with Jython

The following post describes how (with a little hacking) you can use Jython and the standard Java security manager to create a sandboxed environment in which to securely run untrusted Python scripts.

Note: the following was tested with Jython 2.5.3 and Java 1.7u45. The command line examples assume Linux and Bash.

Let's say you've written a Java application, and you want your users to be able to customise it by providing Pyhton scripts that will be run by the application. Let's also say that you have a deep-seated distrust of your users: they're inherently suspect, and any code they supply to be run as part of your application should be treated as if it were written by the Devil himself.

This poses a problem. We would like to be able to use the Java implementation of the Pyhton interpreter, Jython. However, neither Jython not the Java JSR 223 scripting API (javax.script) offer much in the way of security sandboxing features. We know that Java offers excellent sandboxing through the JVM security manager and security policies, so that looks like a prime candidate, but we need to grant sufficient permissions to the Jython interpreter while at the same time placing restrictions on the Python scripts that the interpreter is running on our behalf. Tricky.

As you might expect, the Jython interpreter needs us to grant it some basic permissions in order to run; it needs to be able to load script files and access some Java system properties for instance. All in all, it turns out Jython needs the us to grant it the following permissions (assuming that the Jython jar is in our working directory):

grant codebase "file:${user.dir}/jython-standalone-2.5.3.jar" {
  permission java.lang.RuntimePermission "createClassLoader";
  permission java.lang.RuntimePermission "getProtectionDomain";
  permission java.lang.RuntimePermission "accessDeclaredMembers";
  permission java.io.FilePermission "${user.dir}/*", "read";
  permission java.util.PropertyPermission "java.vm.name", "read";
  permission java.util.PropertyPermission "java.vm.vendor", "read";
  permission java.util.PropertyPermission "os.name", "read";
  permission java.util.PropertyPermission "os.arch", "read";
  permission java.util.PropertyPermission "user.dir", "read";
  permission java.util.PropertyPermission "line.separator", "read";
};

Note: The accessDeclaredMembers permission is only necessary if you need to allow scripts to extend Java classes. Given our scenario, we'll assume that's a likely requirement of our Java application's API.

Great, with the above in our Java security policy file (which we'll cunningly call security.policy), we can run Jython with the Java security manager installed, and the interpreter runs our untrusted script, the imaginatively-titled untrusted-script.py:

java -cp jython-standalone-2.5.3.jar -Djava.security.manager -Djava.security.policy==security.policy org.python.util.jython untrusted-script.py

This has already significantly restricted what our fiendishly evil script can do. For instance, if we try to run the following code:

f = open('/etc/passwd')
userdetails = f.read()
print(userdetails)

We get this error message:

java.security.AccessControlException: java.security.AccessControlException: access denied ("java.io.FilePermission" "/etc/passwd" "read")

Busted! This is great: we know that because the Python script is running in the Jython interpreter, and the Jython interpreter is running in the JVM, our untrusted Python script won't be able to do anything that we haven't granted the Jython jar permission to do.

However, there's still a problem. One of the permissions we had to grant the Jython jar in order for it to run at all was the java.lang.RuntimePermission "createClassLoader", and as it turns out, granting code this permission effectively invalidates your security policy, because code that can create its own classloader can create a classloader that grants full permissions to any classes it loads. From the Java API for RuntimePermission:

"This is an extremely dangerous permission to grant. Malicious applications that can instantiate their own class loaders could then load their own rogue classes into the system. These newly loaded classes could be placed into any protection domain by the class loader, thereby automatically granting the classes the permissions for that domain."

But wait, we're only running Python scripts: what harm can they do with the createClassLoader permission? Well, this is Jython, where Python code can call Java APIs, and do this:

from java.security import AccessControlException, Permissions, AllPermission, SecureClassLoader, CodeSource
from java.net import URL

import base64

class DodgyClassLoader(SecureClassLoader):

  def __init__(self):
    SecureClassLoader.__init__(self)
    self.datamap = {}
    self.codeSource = CodeSource(URL('file:/dummy'), None)

  def addClass(self, name, data):
    self.datamap[name] = data

  def findClass(self, name):
    data = self.datamap[name]
    return self.super__defineClass(name, data, 0, len(data), self.codeSource)

  def getPermissions(self, codesource):
    permissions = Permissions()
    permissions.add(AllPermission())
    return permissions

fileReaderClassDef = base64.b64decode('<<base64 ecoding of a class file defining a class that reads files>>')

fileReaderInnerClassDef = base64.b64decode('<<base64 encoding of the inner class containing the code to read files within a doPrivileged block>>')

classloader = DodgyClassLoader()
classloader.addClass('dodgy.FileReader', fileReaderClassDef)
classloader.addClass('dodgy.FileReader$1', fileReaderInnerClassDef)
fileReaderClass = classloader.findClass('dodgy.FileReader')
fileReader = fileReaderClass.newInstance()
userDetails = fileReader.readFile('/etc/passwd')
print(userDetails)

Confound it all, we were so close. If only there were some way to allow the Jython interpreter permission to create classloaders, while at the same time preventing Python scripts from exploiting this. We could maybe do some filtering of the untrusted scripts to check they're not creating classloaders, but we're now relying on mechanisms outside of our nice safe Java sandbox, and we'd have to account for all manner of Python trickery.

Hold on though: in order to grant code permission to do some sensitive operation, the Java security manager needs every frame on the stack to have the necessary permission. At the moment, we've given all the code in the Jython jar the same set of permissions. What if there were a few classes that aren't on the stack when Jython needs to do its classloading, but are on the stack whenever a script tries to call Java APIs? We could place these classes in a separate jar file and not grant them any permissions that we don't want to grant to our Python scripts, limiting the what scripts can do through the Java APIs.

As luck would have it, there are such classes:
  • org.python.core.PyReflectedConstructor
  • org.python.core.PyReflectedField
  • org.python.core.PyReflectedFunction
Let's pull these out of the Jython jar and put them in their own jar:
unzip jython-standalone-2.5.3.jar org/python/core/PyReflected*
jar cvf jython-standalone-2.5.3-nonsecure.jar org/python/core/*
rm -rf org
zip -d jython-standalone-2.5.3.jar org/python/core/PyReflected*

This removes the classes from the main Jython jar and puts them into a new jar: jython-standalone-2.5.3-nonsecure.jar. As this jar isn't listed in our security policy file, these classes don't get any security permissions. Now we can run Jython by adding this extra jar to the classpath as follows:

java -cp jython-standalone-2.5.3-nonsecure.jar:jython-standalone-2.5.3.jar -Djava.security.manager -Djava.security.policy==security.policy org.python.util.jython untrusted-script.py

This way, if our untrusted script tries to create a classloader, the Jython interpreter runs correctly, but the script throws the following exception:

java.security.AccessControlException: java.security.AccessControlException: access denied ("java.lang.RuntimePermission" "createClassLoader")

This is exactly the behavior we want, and it means that we've closed off the only permission that we had to grant the Jython interpreter that was a major concern. Huzzah!

Now, getting back to out original problem, if we wanted to take advantage of this setup in a Java application, all we'd need to do is set the Java security manager when we start up our application, grant java.security.AllPermissions to our application code, and invoke Jython from Java, most likely with the JSR 223 API.

Notes:
  • The hacking around with jar files is only necessary into order to deal with the createClassLoader permission. This is something that we'd like to avoid doing because of the power it potentially gives to code running in the Jython interpreter. However, it's worth noting that the example exploit above, which creates a dodgy subclass of classloader, is only possible because we also granted the accessDeclaredMembers permission. If there's no requirement to allow scripts to extend Java classes, this permission isn't necessary, and it becomes much harder to exploit the createClassLoader permission.
Shortcomings:
  • We had to grant Jython permission to read files in the working directory in order to read the script file, and the tweaks we made to the jar files don't block access to scripts. We therefore shouldn't put anything important in the working directory, or anywhere we intend to put script files.
  • For some reason I've not got to the bottom of yet, these changes prevent Jython running up a command line, which isn't too much of a problem as we want to supply a script, rather than interact with Python via the command line.
  • This is something of a hack and very susceptible to changes in the Jython source code.