2014-09-24 7 views
6

Jestem całkiem nowy dla groovy i skryptów w java ogólnie, i naprawdę mam nadzieję, że istnieje proste rozwiązanie mojego problemu. W naszej aplikacji użytkownicy mogą uruchamiać groovy skrypty, które piszą samodzielnie, i musimy kontrolować, co te skrypty mogą i nie mogą zrobić. Dużo czytam na temat piaskownicy, ale albo patrzę na niewłaściwych miejsc, albo przeoczyłem oczywistość. Aby było prostsze, mam mały przykład demonstrujący problem. To moja klasa ładowarka, która powinna zapobiec java.lang.System od bycia załadowany i dostępne dla skryptów:Jak zablokować dostęp do niektórych klas podczas wykonywania groovy skryptów z java?

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

A to prosty program, który próbuje wywołać 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 generuje wyjątki dla java.lang.SystemBeanInfo i java.lang.SystemCustomizer, ale kod zostanie wykonany. samo dzieje się, jeśli mogę użyć javax.script klasy:

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

A jeśli próbuję go z silnikiem JavaScriptu, to działa zgodnie z oczekiwaniami (wystarczy wymienić „Groovy” z „Javascript” w powyższym przykładzie).

Czy ktoś może mi w tym pomóc? BTW, używam groovy-all-1.8.8.jar, z jdk1.7.0_55.

Dzięki

+0

Działa to samo z groovy v.2.3.6 i java 1.8.0_05. 'java.lang.System' nie jest nawet załadowany tym programem ładującym klasy. – Opal

+0

@Opal Ten moduł ładujący próbuje załadować 'java.lang.SystemBeanInfo' i' java.lang.SystemCustomizer', który, jak zakładam, wykorzystuje do zawijania 'java.lang.System'. Ale ma sens, że inny program ładujący klasy jest w użyciu, niezależnie od 'MyClassLoader' nie ma nadrzędnego programu ładującego klasy, po prostu nie wiem jak to działa i jak dostosować to zachowanie. –

Odpowiedz

3

Mogę w tym celu polecić Groovy Sandbox. W przeciwieństwie do SecureASTCustomizer sprawdza, czy wykonanie jest dozwolone dynamicznie w czasie wykonywania. Przechwytuje wszystkie wywołania metod, przydziały obiektów, dostęp do właściwości/atrybutów, dostęp do macierzy itd. - a zatem masz bardzo drobną kontrolę nad tym, co pozwalasz (białe listy).

Oczywiście konfiguracja dozwolona jest bardzo ważna. Na przykład możesz zezwolić na używanie String s i użyć metod takich jak substring, ale prawdopodobnie nie na execute na String, które mogą być wykorzystane z czymś takim jak 'rm -R ~/*'.execute(). Tworzenie konfiguracji, która jest naprawdę bezpieczna, jest wyzwaniem i jest trudniejsze, im więcej pozwalasz.

Wadą Groovy Sandbox jest to, że kod musi działać z zarejestrowanym przechwytywaczem, a podczas wykonywania zostanie nałożona kara za wydajność.

Ten obraz [1] pokazuje przykład z projektu, w którym wykorzystaliśmy Groovy Sandbox dla kodu Groovy wprowadzonego przez użytkownika.Kod jest uruchamiany na valide skrypt - więc jeśli oświadczenie nie będzie faktycznie zrealizowanej w ramach niego, wniosek byłby wyszedł zanim mogłem zrobić zrzut ekranu;)

Example from a project where we used Groovy Sandbox

+0

Czytałem o Groovy Sandbox, ale nigdy go nie wypróbowałem, sprawdzę to. Jednakże, jak już wspomniałem w innym komentarzu, mamy już niestandardowy program ładujący klasy, który działa na inne obsługiwane przez nas silniki skryptowe, więc preferowanym rozwiązaniem byłoby ponowne użycie go również w celu uzyskania groovy. –

+1

Może konfiguracja niestandardowego programu ładującego klasy, który klasy pozwalają/zabrania, może być ponownie użyta w konfiguracji Groovy Sandbox. Groovy może wymagać specjalnego traktowania, ponieważ rozszerza klasy JDK o dodatkowe funkcje, które mogą stwarzać problemy bezpieczeństwa na klasach, które są bezpiecznie być dozwolonym w Javie (String.execute to tylko jeden przykład) – stempler

+0

Zgadzam się, myślałem tak samo, już modyfikując kod. Mam jeszcze jeden eksperyment z programem ładującym klasy, a jeśli się nie uda, przyjmuję tę odpowiedź. –

2

Może byłbyś zainteresowany innymi SecureASTCustomizer w połączeniu z CompilerConfiguration. Jeśli chodzi o bezpieczeństwo, wyraźna biała lista może być lepsza niż czarna.

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

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

def sh = new GroovyShell(c) 

Spójrz na tę klasę, zawiera wiele opcji, które są gotowe do użycia.

+0

To zdecydowanie pomaga, ale nie obejmuje wszystkich moich potrzeb. Na przykład mogę uniemożliwić użycie 'java.lang.System', jak w powyższym przykładzie, używając' importsBlacklist' i 'c.setIndirectImportCheckEnabled (true)', ale to nadal będzie działało: 'Class.forName (" java .lang.System "). getMethod (" currentTimeMillis ", null) .invoke (null)'. Inna rzecz, mamy już nasz niestandardowy program ładujący klasy, który jest używany (i działa) dla innych obsługiwanych przez nas silników skryptów (javascript i python), więc wolałbym, aby było to możliwe za pomocą niestandardowego programu ładującego klasy. –

+0

Oto wskazówka, jeśli wykonam to: 'def cl = this.class.classLoader; def threadCl = Thread.currentThread(). ContextClassLoader; ' Otrzymam odpowiednio' groovy.lang.GroovyClassLoader' oraz 'my.package.MyClassLoader'. Zgaduję więc, że GroovyClassLoader ładuje klasę System, po prostu nie wiem, czy i jak mogę na nią wpływać. –

+1

@PredragMaric Hej, rozumiem twój punkt widzenia. Niestety nie wiem zbyt wiele o klasycznych ładowarkach Groovy (jeszcze) .. – 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(); 

} 
} 

Czy go czego chcesz ?

Powiązane problemy