Search code examples
javagroovyclassloadersandbox

How to block access to some classes, when executing groovy scripts from java?


I'm pretty new to groovy, and scripting in java generally, and I really hope there is a simple solution for my problem. In our application, the users can execute groovy scripts which they write themselves, and we need to control what those scripts can and can not do. I read a lot of stuff about sandboxing groovy, but either I am looking at wrong places or I am overlooking the obvious. To make it simple, I have a small example which demonstrates the problem. This is my class loader which should prevent java.lang.System from being loaded and available to scripts:

public class MyClassLoader extends ClassLoader {

    @Override
    public Class<?> loadClass(String name) throws ClassNotFoundException {
        if (name.startsWith("java.lang.System")) {
            throw new ClassNotFoundException("Class not found: " + name);
        }
        return super.loadClass(name);
    }
}

And this is a simple program that tries to call System.currentTimeMillis():

public static void main(String[] args) {
    String code = "java.lang.System.currentTimeMillis();";
    ClassLoader classLoader = new MyClassLoader();
    Thread.currentThread().setContextClassLoader(classLoader);

    GroovyShell shell = new GroovyShell();
    Script script = shell.parse(code);
    Object result = script.run();
    log.debug(result);
}

MyClassLoader throws exceptions for java.lang.SystemBeanInfo and java.lang.SystemCustomizer, but the code executes. Same thing happens if I use javax.script classes:

ScriptEngineManager factory = new ScriptEngineManager();
ScriptEngine engine = factory.getEngineByName("Groovy");
Object o = engine.eval(code);
log.debug(o);

And if I try it with JavaScript engine, it works as expected (just replace "Groovy" with "JavaScript" in the above example).

Can anyone help me with this? BTW, I'm using groovy-all-1.8.8.jar, with jdk1.7.0_55.

Thanks


Solution

  • I can recommend Groovy Sandbox for this purpose. In contrast to SecureASTCustomizer it will check if an execution is allowed dynamically at runtime. It intercepts every method call, object allocations, property/attribute access, array access, and so on - and you thus have a very fine grained control on what you allow (white-listing).

    Naturally the configuration on what is allowed is very important. For example you may want to allow using Strings and use methods like substring, but probably not the execute method on String, which could be exploited with something like 'rm -R ~/*'.execute(). Creating a configuration that is really safe is a challenge, and it is more difficult the more you allow.

    Downside of the Groovy Sandbox is that the code must run with the interceptor registered and you will have a performance penalty during execution.

    This image [1] shows an example from a project where we used Groovy Sandbox for Groovy code entered by the user. The code is run to valide the script - so if the statement there would actually be executed as part of it, the application would have exited before I could do the screenshot ;)

    Example from a project where we used Groovy Sandbox