Hello! I am a security-conscious Java developer and am interested in using the JVMs built-in security features to run code in separated and run-time configured sandboxes. I'm writing to the list to explain some of the issues I've come up against and am hoping to either elicit suggestions or at least provoke some discussion about how the JVM might better support this.
I've been working on a small experimental system for sandboxing due to dissatisfaction with the existing sandboxing packages. The existing sandboxing packages appear to be overly complicated, fragile, and unmaintained. They almost all implement a complicated and error-prone custom security manager and seem to more or less ignore everything else the JVM has in terms of security features. I'm hoping that I can do better! My own use case will be running code that is sandbox-aware and that only uses a few classes from java.lang and talks to an API that I provide to each sandbox. I would expect to restrict arbitrary file I/O (with sandboxed code persisting state via provided key/value interface), restrict network I/O, restrict access to native code, restrict access to reflection, restrict thread creation, and restrict exiting the VM. About the only thing I cannot protect against is heap exhaustion (but the JVM does a decent job of enforcing a global limit anyway, so it's not as if malicious code would end up killing the user's machine or running afoul of operating system limits). It seems that others have somewhat similar use cases, often using some sort of sandbox to provide security to embedded languages that have been compiled to JVM bytecode at run-time. I won't bore anyone here with the details of how the JVM applies security policy because I'd assume everyone on this list already understands it. My basic approach has been to use a custom implementation of java.security.Policy[0] and a custom classloader. The program creates one classloader C per application sandbox S and assigns all classes loaded by C a protection domain P. My assumption is that for a particular sandbox, we no longer care about fine-grained per-CodeSource control of classes inside the sandbox as we're more likely to be applying a coarse sandbox-wide set of restrictions. This then means that that the custom Policy implementation can assign permissions on a per-sandbox basis by simply checking the CodeSource URL and returning any permissions defined for that URL. As a concrete example, I create a sandbox that I then assign a URL of http://sandbox.io7m.com/1. The classloader for that sandbox assigns every loaded class a CodeSource with location http://sandbox.io7m.com/1. Now, whenever the AccessController consults the policy's checkPermission function, the policy simply uses the set of permissions defined for http://sandbox.io7m.com/1. As an aside, I do use a custom SecurityManager but only to add a couple of extra checks for Thread and ThreadGroup creation/modification, because the default SecurityManager is not strict enough. This appears to work well. I've been unable to subvert the sandbox and am reasonably confident in its security simply due to the fact that it does absolutely nothing clever whatsoever and uses the basic provided JVM security features to achieve it. The code is less than 150 lines and is not exciting in any way. The bulk of a real implementation would be providing a pleasant API and a nice way to configure policies at run-time. My main gripes: 1. The ClassLoader and SecureClassLoader classes are not very nice. It seems that I cannot take an existing classloader and preserve all of the semantics with regards to mapping names to byte arrays (such as looking through the classpath for class files, contacting remote servers for classes, etc) if I want to maintain my own control over the resulting ProtectionDomains of those classes. It is likely that ProtectionDomains and CodeSources were never intended to be used in the slightly abusive way I'm using them in the above system. I'm guessing also that the implementations carry a ton of historical baggage and would likely not have their interfaces presented in the way they currently are if they were written/designed today! There is a tempting package-private method in java.lang.Class called setProtectionDomain that I'm not allowed to call. Having access to this would allow me use any existing class loader and simply overwrite the protection domains of the resulting classes without having to modify any code. 2. I feel like I should not have to do any of the things I have done! I realize this sounds silly, but if it were possible to label classes with a simple immutable opaque tag indicating their confinement, and the Policy could refer to this tag... I'd already be done. I would assume that setting a confinement label on a class would require security checks and that it could only be set once. This seems almost too good/simple an approach to be true - would it require an unlimited amount of bureaucracy to get something like this into the standard library? It obviously exists to support a specific use case, but I could see how the same approach could be used to provide a generalized sandbox for anything compiled to JVM bytecode running inside a scripting engine, for example. I'm open to flames and/or suggestions on better approaches. Mark -- [0] Not essential for sandboxing, but essential for run-time configuration, because the default Policy is obviously loaded from a file on JVM startup, can't be easily/pleasantly updated, and likely references a policy file that contains system-specific defaults that I'd like to ignore!