2011-07-01 2 views
4

JEXL 스크립트를 실행하기위한 샌드 박스를 만들어서 악의적 인 사용자가 액세스 권한을 부여한 변수 외부의 데이터에 액세스 할 수 없으며 서버에서 DOS 공격을 수행 할 수도 없습니다. 나는 이것을 다른 사람에게도 문서화하고 다른 사람들의 의견을 접근법에 넣고 싶습니다.안전한 JEXL (스크립팅) 샌드 박스는 어떻게 만듭니 까?

다음은 내가 그 알고 있어요 것들의 목록이 해결 될 필요가있다 :

  1. 만 화이트리스트에있는 그 '새'를 사용하여 클래스를 인스턴스화 할 수 있습니다.
  2. forName을 호출 할 수 있고 모든 클래스에 액세스 할 수 있으므로 모든 클래스에서 getClass 메서드에 액세스 할 수 없도록 설정하십시오.
  3. 파일과 같은 리소스에 대한 액세스를 제한합니다.
  4. 표현식이 실행되는 데 일정한 시간 만 허용되므로 소비하는 리소스의 양을 제한 할 수 있습니다.

이 JEXL 적용되지 않지만 사용하는 스크립트 언어에 적용 할 수 있습니다 finalize 메소드가 종료 자 스레드에서 호출되기 때문에

  1. 사용자 정의 finalize 메소드를 가지고 객체를 허용하지 않음 객체를 생성 해, 그 객체 내의 코드를 실행하기 위해서 (때문에) 사용 된 AccessControlContext 대신에, 원래의 AccessControlContext로 실행됩니다.

답변

6

여기 각각의 경우에 대한 나의 접근 방식입니다. 나는이 각각의 경우를 테스트하기 위해 단위 테스트를 만들었고, 그것들이 작동하는지 확인했다.

  1. JEXL은 매우 쉽게 만듭니다. 커스텀 ClassLoader를 생성하면됩니다. 2 개의 loadClass() Methods를 재정의 (override)합니다. JexlEngine에서 setClassLoader()를 호출하십시오.

  2. 다시 말하지만, JEXL은 이것을 매우 쉽게 만듭니다. '.class'와 '.getClass()'를 모두 차단해야합니다. UberspectImpl을 확장 한 자체 Uberspect 클래스를 만듭니다. 식별자가 "class"가 null 인 경우는 getPropertyGet를 오버라이드 (override)합니다. getClass 메소드가 null를 돌려 주었을 경우, getMethod를 오버라이드 (override)합니다. JexlEngine을 구성 할 때 Uberspect 구현에 대한 참조를 전달하십시오.

  3. Java의 AccessController 메커니즘을 사용하여이 작업을 수행합니다. 이렇게 빨리 실행 해 보겠습니다. -Djava.security.policy = policyfile로 java를 시작하십시오. 이 줄을 포함하는 policyfile이라는 파일을 만듭니다. grant {permission java.security.AllPermission; }; 이 호출로 기본 SecurityManager를 설정합니다. System.setSecurityManager (new SecurityManager()); 이제는 권한을 제어 할 수 있으며 기본적으로 모든 권한을 가지고 있습니다. 앱의 권한을 당연히 필요한 것만으로 제한하는 것이 더 낫습니다. 그런 다음 권한을 최소한으로 제한하고 AccessController.doPrivileged()를 호출하고 AccessControlContext를 전달한 다음 doPrivileged() 내부에서 JEXL 스크립트를 실행하는 AccessControlContext를 만듭니다. 여기에 이것을 보여주는 작은 프로그램이 있습니다. JEXL 스크립트는 System.exit (1)을 호출하고 doPrivileged()에 래핑되지 않은 경우 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. 이 트릭은 스크립트가 완료되기 전에 중단되는 것입니다. 이 작업을 수행 한 한 가지 방법은 사용자 지정 JexlArithmetic 클래스를 만드는 것입니다. 그런 다음 해당 클래스의 각 메소드를 대체하고 스크립트가 실행을 중지해야하는지 여부를 수퍼 클래스 검사에서 실제 메소드를 호출하기 전에 확인하십시오. ExecutorService를 사용하여 스레드를 만듭니다. 미래.get()은 기다릴 시간을 전달합니다. TimeoutException이 throw되면 스크립트를 실행하는 스레드를 인터럽트하는 Future.cancel()을 호출합니다. 새로운 JexlArithmetic 클래스의 오버라이드 (override) 된 각 메소드의 내부에서, Thread.interrupted()를 체크해, true의 경우는 java.util.concurrent.CancellationException를 Throw합니다. 중단 될 수 있도록 스크립트가 실행될 때 정기적으로 실행되는 코드를 넣을 수있는 더 좋은 위치가 있습니까?