commons-user mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Sarel Botha <sa...@botha.us>
Subject [jexl] JEXL Secure Sandbox
Date Mon, 27 Jun 2011 04:37:21 GMT
I'm creating a sandbox for JEXL scripts to execute in so that a 
malicious user can't access data outside the variables we give them 
access to and also can't perform a DOS attack on the server. I'd like to 
document this for anybody else also doing this and also get other 
people's input into the approach.

The following is a list of the things I'm aware of that needs to be 
addressed:
  1. Only allow instantiating classes using 'new' that are on a whitelist.
  2. Do not allow accessing the getClass method on any class because 
then forName can be called and any class can be accessed.
  3. Restrict access to resources such as files.
  4. Allow an expression only a certain amount of time to execute so 
that we can limit the amount of resources it consumes.

Here is my approach for dealing with each of these cases. I've created 
unit tests to test each of these cases and I have verified that they work.

1. JEXL makes this pretty easy. Just create a custom ClassLoader. 
Override the two loadClass() methods. On JexlEngine call setClassLoader().

2. Again, JEXL makes this pretty easy. You must block both '.class' and 
'.getClass()'. Create your own Uberspect class which extends 
UberspectImpl. Override getPropertyGet, if identifier equals "class" 
return null. Override getMethod, if method equals "getClass" return 
null. When constructing JexlEngine pass a reference to your Uberspect 
implementation.

3. You do this using Java's AccessController mechanism. I'll give a 
quick run-down of doing this. Start java with 
-Djava.security.policy=policyfile. Make a file named policyfile 
containing this line:
grant { permission java.security.AllPermission; };
Set the default SecurityManager with this call: 
System.setSecurityManager(new SecurityManager()); Now you can control 
permissions and your app by default has all permissions. It would be 
better if you limit the permissions of your app to only what it requires 
of course. Next, create an AccessControlContext that limits the 
permissions to the bare minimum and call AccessController.doPrivileged() 
and pass the AccessControlContext, then execute the JEXL script inside 
doPrivileged(). Here is a small program that demonstrates this. The JEXL 
script calls System.exit(1) and if it isn't wrapped in doPrivileged() it 
would successfully terminate the JVM.
-----------
         System.out.println("java.security.policy=" + 
System.getProperty("java.security.policy"));
         System.setSecurityManager(new SecurityManager());
         try {
             Permissions perms = new Permissions();
             perms.add(new RuntimePermission("accessDeclaredMembers"));
             ProtectionDomain domain = new ProtectionDomain(new 
CodeSource( null, (Certificate[]) null ), perms );
             AccessControlContext restrictedAccessControlContext = new 
AccessControlContext(new ProtectionDomain[] { domain } );

             JexlEngine jexlEngine = new JexlEngine();
             final Script finalExpression = jexlEngine.createScript(
                     "i = 0; intClazz = i.class; "
                     + "clazz = intClazz.forName(\"java.lang.System\"); "
                     + "m = clazz.methods; m[0].invoke(null, 1); c");

             AccessController.doPrivileged(new 
PrivilegedExceptionAction<Object>() {
                 @Override
                 public Object run() throws Exception {
                     return finalExpression.execute(new MapContext());
                 }
             }, restrictedAccessControlContext);
         }
         catch (Throwable ex) {
             ex.printStackTrace();
         }
-----------

4. The trick with this is interrupting the script before it finishes. 
One way I found to do this is to create a custom JexlArithmetic class. 
Then override each method in that class and before calling the real 
method in the super class check if the script should stop executing. I'm 
using an ExecutorService to create threads. When Future.get() is called 
pass the amount of time to wait. If a TimeoutException is thrown call 
Future.cancel() which interrupts the Thread running the script. Inside 
each overridden method in the new JexlArithmetic class check 
Thread.interrupted() and if true throw 
java.util.concurrent.CancellationException.

Is there a better location to put code which will get executed regularly 
as a script is being executed so that it can be interrupted?

Thanks,
Sarel


---------------------------------------------------------------------
To unsubscribe, e-mail: user-unsubscribe@commons.apache.org
For additional commands, e-mail: user-help@commons.apache.org


Mime
View raw message