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

早过忘川 提交于 2019-12-08 20:51:37

问题


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


回答1:


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 ;)




回答2:


Perhaps you'd be interested in using a SecureASTCustomizer in conjunction with a CompilerConfiguration. If you are concerned with security, an explicit white list might be better than a black list.

def s = new SecureASTCustomizer()
s.importsWhiteList = [ 'a.legal.Klass', 'other.legal.Klass' ]

def c = new CompilerConfiguration()   
c.addCompilationCustomizers(s)

def sh = new GroovyShell(c)

Take a look at that class, it contains a lot of options that are ready to use.




回答3:


import groovy.lang.GroovyClassLoader;
import groovy.lang.GroovyShell;
import groovy.lang.Script;

public class SandboxGroovyClassLoader extends ClassLoader {

public SandboxGroovyClassLoader(ClassLoader parent) {
    super(parent);
}

@Override
public Class<?> loadClass(String name) throws ClassNotFoundException    {
    if (name.startsWith("java.lang.System"))
        return null;
    return super.loadClass(name);
}

@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
    if (name.startsWith("java.lang.System"))
        return null;
    return super.loadClass(name, resolve);
}

static void runWithGroovyClassLoader() throws Exception {
    System.out.println("Begin runWithGroovyClassLoader");

    String code = "def hello_world() { java.lang.System.currentTimeMillis(); };";

    GroovyClassLoader groovyClassLoader = new GroovyClassLoader();
    Class<?> scriptClass = groovyClassLoader.parseClass(code);
    Object scriptInstance = scriptClass.newInstance();
    Object result = scriptClass.getDeclaredMethod("hello_world", new Class[] {}).invoke(scriptInstance, new Object[] {});
    System.out.println(result);
    groovyClassLoader.close();
    System.out.println("End runWithGroovyClassLoader");
}

static void runWithSandboxGroovyClassLoader() throws Exception {
    System.out.println("Begin runWithSandboxGroovyClassLoader");
    ClassLoader parentClassLoader = SandboxGroovyClassLoader.class.getClassLoader();
    SandboxGroovyClassLoader classLoader = new SandboxGroovyClassLoader(parentClassLoader);

    String code = "def hello_world() { java.lang.System.currentTimeMillis(); };";

    GroovyClassLoader groovyClassLoader = new GroovyClassLoader(classLoader);
    Class<?> scriptClass = groovyClassLoader.parseClass(code);
    Object scriptInstance = scriptClass.newInstance();
    Object result = scriptClass.getDeclaredMethod("hello_world", new Class[] {}).invoke(scriptInstance, new Object[] {});
    System.out.println(result);
    groovyClassLoader.close();
    System.out.println("End runWithSandboxGroovyClassLoader");
}

static void runWithSandboxGroovyShellClassLoader() throws Exception {
    System.out.println("Begin runWithSandboxGroovyShellClassLoader");

    String code = "java.lang.System.currentTimeMillis();";

    ClassLoader parentClassLoader = SandboxGroovyClassLoader.class.getClassLoader();
    SandboxGroovyClassLoader classLoader = new SandboxGroovyClassLoader(parentClassLoader);
    Thread.currentThread().setContextClassLoader(classLoader);

    GroovyShell shell = new GroovyShell();
    Script script = shell.parse(code);
    Object result = script.run();
    System.out.println(result);
    System.out.println("End runWithSandboxGroovyShellClassLoader");
}

public static void main(String[] args) throws Exception {

    runWithGroovyClassLoader();
    runWithSandboxGroovyClassLoader();
    runWithSandboxGroovyShellClassLoader();

}
}

Is it what you want ?



来源:https://stackoverflow.com/questions/26017584/how-to-block-access-to-some-classes-when-executing-groovy-scripts-from-java

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!