2014-09-24 2 views
6

Ich bin ziemlich neu zu groovy, und Skripte in Java im Allgemeinen, und ich wirklich hoffe, es gibt eine einfache Lösung für mein Problem. In unserer Anwendung können die Benutzer groovy Skripte ausführen, die sie selbst schreiben, und wir müssen steuern, was diese Skripte können und was nicht. Ich lese viele Dinge über Sandboxing groovy, aber entweder ich sehe falsche Orte oder ich übersehe das Offensichtliche. Um es einfach zu machen, habe ich ein kleines Beispiel, das das Problem zeigt. Das ist mein Klassenlader die java.lang.System entfernt, geladen und verfügbar zu Skripten verhindern sollten:Wie blockiert man den Zugriff auf einige Klassen, wenn man groovige Skripte von Java ausführt?

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

Und das ist ein einfaches Programm, das zu nennen versucht 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 wirft Ausnahmen für java.lang.SystemBeanInfo und java.lang.SystemCustomizer, aber der Code wird ausgeführt. Gleiche passiert, wenn ich javax.script Klassen verwenden:

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

Und wenn ich es mit JavaScript-Engine versuchen, es funktioniert wie erwartet (nur „Groovy“ mit „JavaScript“ in dem obigen Beispiel ersetzen).

Kann mir jemand dabei helfen? BTW, ich benutze groovy-all-1.8.8.jar, mit jdk1.7.0_55.

Dank

+0

Es funktioniert gleich mit groovy v.2.3.6 und Java 1.8.0_05. 'java.lang.System' ist nicht einmal mit diesem Classloader geladen. – Opal

+0

@Opal Dieser Classloader versucht 'java.lang.SystemBeanInfo' und' java.lang.SystemCustomizer' zu laden, was, wie ich annehme, groovy für das Wrapping 'java.lang.System' verwendet. Aber es macht Sinn, dass ein anderer Classloader verwendet wird, unabhängig davon, ob "MyClassLoader" keinen Eltern-Classloader hat, ich weiß einfach nicht, wie es funktioniert und wie man dieses Verhalten anpasst. –

Antwort

3

Ich kann Groovy Sandbox für diesen Zweck empfehlen. Im Gegensatz zu SecureASTCustomizer wird geprüft, ob eine Ausführung zur Laufzeit dynamisch erlaubt ist. Es fängt alle Methodenaufrufe, Objektzuordnungen, Eigenschaften-/Attributzugriffe, Array-Zugriffe usw. ab - und Sie haben somit eine sehr genaue Kontrolle darüber, was Sie zulassen (White-Listing).

Natürlich ist die Konfiguration, was erlaubt ist, sehr wichtig. Zum Beispiel möchten Sie möglicherweise zulassen, String s verwenden und Methoden wie substring, aber wahrscheinlich nicht die execute Methode auf String, die mit etwas wie 'rm -R ~/*'.execute() ausgenutzt werden könnte. Das Erstellen einer Konfiguration, die wirklich sicher ist, ist eine Herausforderung, und es ist schwieriger, je mehr Sie zulassen.

Nachteil der Groovy Sandbox ist, dass der Code mit dem Interceptor registriert ausgeführt werden muss und Sie eine Leistungseinbuße während der Ausführung haben.

Dieses Bild [1] zeigt ein Beispiel aus einem Projekt, in dem wir Groovy Sandbox für Groovy-Code verwendet haben, der vom Benutzer eingegeben wurde.Der Code wird das Skript Valide - also, wenn die Aussage dort tatsächlich als ein Teil davon ausgeführt werden würde, würde die Anwendung beendet haben, bevor ich den Screenshot machen konnte;)

Example from a project where we used Groovy Sandbox

+0

Ich lese über Groovy Sandbox, habe es aber nie ausprobiert, ich werde es ausprobieren. Wie ich bereits in einem anderen Kommentar gesagt habe, haben wir bereits einen benutzerdefinierten Classloader, der für andere von uns unterstützte Skript-Engines funktioniert, daher wäre die bevorzugte Lösung, wenn wir ihn auch für Groovy wiederverwenden können. –

+1

Vielleicht könnte die Konfiguration Ihres benutzerdefinierten Klassenladeprogramms, dessen Klassen es erlaubt/verbietet, in einer Groovy Sandbox-Konfiguration wiederverwendet werden.Groovy benötigt möglicherweise eine spezielle Behandlung, da es die JDK-Klassen um zusätzliche Funktionen erweitert, die Sicherheitsprobleme bei Klassen verursachen können Safe in Java erlaubt sein (String.execute ist nur ein Beispiel) – stempler

+0

Ich stimme zu, ich dachte in die gleiche Richtung, bereits den Code ändern. Ich habe noch ein Experiment mit Klassenlader, und wenn es scheitert, werde ich diese Antwort akzeptieren. –

2

Vielleicht möchten Sie mit einem CompilerConfiguration bei der Verwendung eines SecureASTCustomizer in Verbindung interessiert sein. Wenn Sie sich mit Sicherheit befassen, ist eine explizite Whitelist möglicherweise besser als eine Blacklist.

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

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

def sh = new GroovyShell(c) 

Werfen Sie einen Blick auf diese Klasse, es enthält eine Menge von Optionen, die bereit sind zu verwenden.

+0

Das hilft definitiv, aber es deckt nicht alle meine Bedürfnisse ab. Zum Beispiel kann ich verhindern, dass 'java.lang.System' wie im obigen Beispiel verwendet wird, indem' importsBlacklist' und 'c.setIndirectImportCheckEnabled (true)' verwendet werden, aber das würde immer noch funktionieren: 'Class.forName (" java .lang.System "). getMethod (" currentTimeMillis ", null) .invoke (null)'. Eine andere Sache, wir haben bereits unseren benutzerdefinierten Classloader, der für andere Skript-Engines, die wir unterstützen (Javascript und Python), verwendet wird (und funktioniert), also würde ich es bevorzugen, wenn dies mit dem benutzerdefinierten Classloader möglich wäre. –

+0

Hier ist ein Hinweis, wenn ich dies ausführen: 'def cl = this.class.classLoader; def threadCl = Thread.currentThread(). ContextClassLoader; ' Ich bekomme' groovy.lang.GroovyClassLoader' bzw. 'my.package.MyClassLoader'. Ich denke also, GroovyClassLoader lädt die System-Klasse, ich weiß nur nicht, ob und wie ich das beeinflussen kann. –

+1

@PredragMaric Hey, ich verstehe deinen Standpunkt. Leider weiß ich nicht viel über Groovys Classloader (noch). – emesx

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

} 
} 

Ist es was willst du?

Verwandte Themen