IT

Java 애플리케이션의 악성 코드에 대한 샌드 박스

lottoking 2020. 9. 14. 21:21
반응형

Java 애플리케이션의 악성 코드에 대한 샌드 박스


사용자가 서버에서 실행하기 위해 자신의 코드를 실행할 수있는 시뮬레이션 서버 환경에서 사용자가 출시하는 한 코드를 샌드 박스에서 실행하는 것이 유리할 것입니다. 구축 된 구성 요소를 격리하기 위해 다른 VM 계층을 추가하는 대신 JVM 자체를 사용할 수 있기 때문에 원했습니다.

다만 Java 샌드 박스 모델을 사용하여 가능해 보이지만 실행중인 응용 프로그램의 사용자가 활성화하는 동적 방법이 있습니까?


  1. 자체적 인 경우에서 수없는 코드를 실행합니다. 예를 들어 무한 루프 등의 문제를 방지하고 단계를 더 쉽게 만듭니다. 주 행사가 완료 될 때까지 기다리도록하고, 너무 오래 걸리면 Thread.stop으로 종료합니다. Thread.stop은 더 이상 사용하지 않는 많은 수없는 코드는 리소스에 액세스 할 수있는 인증 죽일 수 있습니다.

  2. 해당 행사에 SecurityManager설정하십시오 . checkPermission (Permission perm)재정의하는 SecurityManager의 하위 클래스 를 만들어 일부 권한을 모든 권한에 대해 SecurityException 을 발생시킵니다. 여기에 메소드 및 필요한 권한 목록이 있습니다 . Java TM 6 SDK의 권한 .

  3. 사용자 정의 ClassLoader를 사용하여 수없는 코드를로드하십시오. 클래스 로더는 수없는 코드가 사용하는 모든 클래스에 대해 호출부터 호출되는 JDK 클래스에 대한 액세스를 사용하는 등의 작업을 수행 할 수 있습니다. 할 일은 허용되는 JDK 클래스의 화이트리스트를위한 것입니다.

  4. 별도의 JVM에서 수없는 코드를 사용할 수 있습니다. 이전 단계에서는 코드를 안전하게 만들 수 있으며, 격리 된 코드가 여전히 할 수있는 한 가지 성가신 일이 있습니다. 가능한 한 많은 메모리를 할당하여 메인 애플리케이션의 가시적 인 공간을 늘리는 것입니다.

JSR 121 : Application Isolation API Specification 은이 설계 설계 안타깝게도 아직 구현되지 않은 문제입니다.

이것은 매우 상세한 주제이며, 나는 대부분이 모든 것을 머리 위에서 쓰고 있습니다.

그러나 어쨌든 불완전하고 위험에 따라 사용하고 버그가있는 (의사) 코드가 있습니다.

ClassLoader

class MyClassLoader extends ClassLoader {
  @Override
  public Class<?> loadClass(String name) throws ClassNotFoundException {
    if (name is white-listed JDK class) return super.loadClass(name);
    return findClass(name);
  }
  @Override
  public Class findClass(String name) {
    byte[] b = loadClassData(name);
    return defineClass(name, b, 0, b.length);
  }
  private byte[] loadClassData(String name) {
    // load the untrusted class data here
  }
}

보안 관리자

class MySecurityManager extends SecurityManager {
  private Object secret;
  public MySecurityManager(Object pass) { secret = pass; }
  private void disable(Object pass) {
    if (pass == secret) secret = null;
  }
  // ... override checkXXX method(s) here.
  // Always allow them to succeed when secret==null
}

class MyIsolatedThread extends Thread {
  private Object pass = new Object();
  private MyClassLoader loader = new MyClassLoader();
  private MySecurityManager sm = new MySecurityManager(pass);
  public void run() {
    SecurityManager old = System.getSecurityManager();
    System.setSecurityManager(sm);
    runUntrustedCode();
    sm.disable(pass);
    System.setSecurityManager(old);
  }
  private void runUntrustedCode() {
    try {
      // run the custom class's main method for example:
      loader.loadClass("customclassname")
        .getMethod("main", String[].class)
        .invoke(null, new Object[]{...});
    } catch (Throwable t) {}
  }
}

모든 종류의 보안 문제를 제기합니다. Java에는 엄격한 보안 프레임이 없습니다. 망쳐 권한이없는 사용자가 중요한 시스템 구성 요소에 액세스 할 가능성을 간과 가능성이 있습니다.

그 경고는 제쳐두고, 소스 코드 형태로 사용자 입력을받는 경우 가장 먼저해야 할 일 일일입니다. AFIAK, 이것은 기본적으로 수행 할 수있는 javac에 대한 시스템 호출을 수행하고 소스 코드를 디스크의 바이트 코드로 수행해야합니다. 여기 에이를위한 시작점으로 사용할 수있는 튜토리얼이 있습니다. 편집 : 주석에서 배웠 듯이 실제로 javax.tools.JavaCompiler를 사용하여 소스에서 자바 코드를 실제로 컴파일 할 수 있습니다.

JVM 바이트 코드가 있으면 ClassLoader의 defineClass 함수를 사용하여 JVM으로로드 할 수 있습니다 . 이로 드 된 클래스에 대한 보안 비용설정해야하는 경우 ProtectionDomain 을 지정합니다 . ProtectionDomain에 대한 최소 생성자 에는 CodeSource 및 PermissionCollection 둘 다 필요합니다 . PermissionCollection은 여기에서 기본적으로 사용되는 개체입니다.이를 사용하여로드 된 클래스가있는 편리한 권한을 사용할 수 있습니다. 충분한 권한은 JVM의 AccessController에 의해 실행되어야합니다 .

여기에는 많은 오류 지점이 즉시 구현하기 전에 모든 것을 완전히 이해해야합니다.


자바 샌드 권한 권한 자바 코드를 실행하는 라이브러리이다. 화이트리스트에있는 클래스 및 리소스 집합에만 액세스를 허용하는 데 사용할 수 있습니다. 인출 방법에 대한 액세스를 제한 할 수없는 것입니다. 이를 위해 사용자 정의 클래스 로더 및 보안 관리자가있는 시스템을 사용합니다.

나는 그것을 사용하지 않습니다. 그리고 합리적으로 잘 문서화되어 있습니다.

@waqas는 어떻게 자신을 구현할 수 있는지 설명하는 매우 흥미로운 정보를 제공했습니다. 그러나 그러나 보안에 중요하고 복잡한 코드를 전문가에게 맡기는 것이 훨씬 안전합니다.

프로젝트는 2013 년 이후로 업데이트되지 않았으며 제작자는 "실험적이라고 설명합니다. 홈페이지는 사라졌지 만 Source Forge 항목은 남아 있습니다.

프로젝트 웹 사이트에서 수정 된 예제 코드 :

SandboxService sandboxService = SandboxServiceImpl.getInstance();

// Configure context 
SandboxContext context = new SandboxContext();
context.addClassForApplicationLoader(getClass().getName());
context.addClassPermission(AccessType.PERMIT, "java.lang.System");

// Whithout this line we get a SandboxException when touching System.out
context.addClassPermission(AccessType.PERMIT, "java.io.PrintStream");

String someValue = "Input value";

class TestEnvironment implements SandboxedEnvironment<String> {
    @Override
    public String execute() throws Exception {
        // This is untrusted code
        System.out.println(someValue);
        return "Output value";
    }
};

// Run code in sandbox. Pass arguments to generated constructor in TestEnvironment.
SandboxedCallResult<String> result = sandboxService.runSandboxed(TestEnvironment.class, 
    context, this, someValue);

System.out.println(result.get());

어떤 제안이나 해결을 제시하는 것이 문제에 직면하고 있습니다. 기본적으로 저는 e- 러닝 플랫폼에서 Java 코스에 대한 프로그래밍 과제를 제공하고 자동 평가를 제공했습니다.

  1. 한 가지 방법은 별도의 가상 머신 (JVM 아님)을 생성하지만 각 학생에 대해 가능한 최소 구성 OS를 사용하는 가상 머신을 만드는 것입니다.
  2. 설치하고 실행하기를 원하는 프로그래밍 언어에 따라 Java 용 JRE 또는 라이브러리를 설치합니다.

이것이 복잡하게 생성하고 많은 작업처럼 들리지만 Oracle Virtual Box는 이미 가상 머신을 동적으로 생성하거나 복제 할 수있는 Java API를 제공합니다. https://www.virtualbox.org/sdkref/index.html (참고, VMware 동일한 작업을 수행하는 API를 제공합니다)

Linux 배포판의 최소 크기 및 구성은 여기 http://www.slitaz.org/en/ 에서 참조 할 수 있습니다 .

따라서 이제는 엉망으로 만들거나 시도하면 메모리 나 파일 시스템 또는 소켓이 소켓이 최대 일 수 있으므로 자신의 VM을 손상시킬 수 있습니다.

또한 내부적으로는 VM에 Java 용 Sandbox (보안 관리자)와 같은 추가 보안을 제공하거나 Linux에서 사용자 별 계정을 생성하여 액세스를 제한 할 수 있습니다.

도움이 되었습니까?


문제에 대한 안전한 솔루션은 다음과 가변적입니다.

https://svn.code.sf.net/p/loggifier/code/trunk/de.unkrig.commons.lang/src/de/unkrig/commons/lang/security/Sandbox.java

package de.unkrig.commons.lang.security;

import java.security.AccessControlContext;
import java.security.Permission;
import java.security.Permissions;
import java.security.ProtectionDomain;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.WeakHashMap;

import de.unkrig.commons.nullanalysis.Nullable;

/**
 * This class establishes a security manager that confines the permissions for code executed through specific classes,
 * which may be specified by class, class name and/or class loader.
 * <p>
 * To 'execute through a class' means that the execution stack includes the class. E.g., if a method of class {@code A}
 * invokes a method of class {@code B}, which then invokes a method of class {@code C}, and all three classes were
 * previously {@link #confine(Class, Permissions) confined}, then for all actions that are executed by class {@code C}
 * the <i>intersection</i> of the three {@link Permissions} apply.
 * <p>
 * Once the permissions for a class, class name or class loader are confined, they cannot be changed; this prevents any
 * attempts (e.g. of the confined class itself) to release the confinement.
 * <p>
 * Code example:
 * <pre>
 *  Runnable unprivileged = new Runnable() {
 *      public void run() {
 *          System.getProperty("user.dir");
 *      }
 *  };
 *
 *  // Run without confinement.
 *  unprivileged.run(); // Works fine.
 *
 *  // Set the most strict permissions.
 *  Sandbox.confine(unprivileged.getClass(), new Permissions());
 *  unprivileged.run(); // Throws a SecurityException.
 *
 *  // Attempt to change the permissions.
 *  {
 *      Permissions permissions = new Permissions();
 *      permissions.add(new AllPermission());
 *      Sandbox.confine(unprivileged.getClass(), permissions); // Throws a SecurityException.
 *  }
 *  unprivileged.run();
 * </pre>
 */
public final
class Sandbox {

    private Sandbox() {}

    private static final Map<Class<?>, AccessControlContext>
    CHECKED_CLASSES = Collections.synchronizedMap(new WeakHashMap<Class<?>, AccessControlContext>());

    private static final Map<String, AccessControlContext>
    CHECKED_CLASS_NAMES = Collections.synchronizedMap(new HashMap<String, AccessControlContext>());

    private static final Map<ClassLoader, AccessControlContext>
    CHECKED_CLASS_LOADERS = Collections.synchronizedMap(new WeakHashMap<ClassLoader, AccessControlContext>());

    static {

        // Install our custom security manager.
        if (System.getSecurityManager() != null) {
            throw new ExceptionInInitializerError("There's already a security manager set");
        }
        System.setSecurityManager(new SecurityManager() {

            @Override public void
            checkPermission(@Nullable Permission perm) {
                assert perm != null;

                for (Class<?> clasS : this.getClassContext()) {

                    // Check if an ACC was set for the class.
                    {
                        AccessControlContext acc = Sandbox.CHECKED_CLASSES.get(clasS);
                        if (acc != null) acc.checkPermission(perm);
                    }

                    // Check if an ACC was set for the class name.
                    {
                        AccessControlContext acc = Sandbox.CHECKED_CLASS_NAMES.get(clasS.getName());
                        if (acc != null) acc.checkPermission(perm);
                    }

                    // Check if an ACC was set for the class loader.
                    {
                        AccessControlContext acc = Sandbox.CHECKED_CLASS_LOADERS.get(clasS.getClassLoader());
                        if (acc != null) acc.checkPermission(perm);
                    }
                }
            }
        });
    }

    // --------------------------

    /**
     * All future actions that are executed through the given {@code clasS} will be checked against the given {@code
     * accessControlContext}.
     *
     * @throws SecurityException Permissions are already confined for the {@code clasS}
     */
    public static void
    confine(Class<?> clasS, AccessControlContext accessControlContext) {

        if (Sandbox.CHECKED_CLASSES.containsKey(clasS)) {
            throw new SecurityException("Attempt to change the access control context for '" + clasS + "'");
        }

        Sandbox.CHECKED_CLASSES.put(clasS, accessControlContext);
    }

    /**
     * All future actions that are executed through the given {@code clasS} will be checked against the given {@code
     * protectionDomain}.
     *
     * @throws SecurityException Permissions are already confined for the {@code clasS}
     */
    public static void
    confine(Class<?> clasS, ProtectionDomain protectionDomain) {
        Sandbox.confine(
            clasS,
            new AccessControlContext(new ProtectionDomain[] { protectionDomain })
        );
    }

    /**
     * All future actions that are executed through the given {@code clasS} will be checked against the given {@code
     * permissions}.
     *
     * @throws SecurityException Permissions are already confined for the {@code clasS}
     */
    public static void
    confine(Class<?> clasS, Permissions permissions) {
        Sandbox.confine(clasS, new ProtectionDomain(null, permissions));
    }

    // Code for 'CHECKED_CLASS_NAMES' and 'CHECKED_CLASS_LOADERS' omitted here.

}

의견을주세요!

CU

아르노


사용자 정의 SecurityManager가 스레드 단위가 아닌 JVM의 모든 스레드에 적용 되는 수락 된 답변의 문제를 해결하려면 SecurityManager다음과 같이 특정 스레드에 대해 활성화 / 비활성화 할 수 있는 사용자 정의 만들 수 있습니다.

import java.security.Permission;

public class SelectiveSecurityManager extends SecurityManager {

  private static final ToggleSecurityManagerPermission TOGGLE_PERMISSION = new ToggleSecurityManagerPermission();

  ThreadLocal<Boolean> enabledFlag = null;

  public SelectiveSecurityManager(final boolean enabledByDefault) {

    enabledFlag = new ThreadLocal<Boolean>() {

      @Override
      protected Boolean initialValue() {
        return enabledByDefault;
      }

      @Override
      public void set(Boolean value) {
        SecurityManager securityManager = System.getSecurityManager();
        if (securityManager != null) {
          securityManager.checkPermission(TOGGLE_PERMISSION);
        }
        super.set(value);
      }
    };
  }

  @Override
  public void checkPermission(Permission permission) {
    if (shouldCheck(permission)) {
      super.checkPermission(permission);
    }
  }

  @Override
  public void checkPermission(Permission permission, Object context) {
    if (shouldCheck(permission)) {
      super.checkPermission(permission, context);
    }
  }

  private boolean shouldCheck(Permission permission) {
    return isEnabled() || permission instanceof ToggleSecurityManagerPermission;
  }

  public void enable() {
    enabledFlag.set(true);
  }

  public void disable() {
    enabledFlag.set(false);
  }

  public boolean isEnabled() {
    return enabledFlag.get();
  }

}

ToggleSecurirtyManagerPermissionjava.security.Permission인증 된 코드 만 보안 관리자를 활성화 / 비활성화 할 수 있도록 하는 간단한 구현입니다 . 다음과 같이 보입니다.

import java.security.Permission;

public class ToggleSecurityManagerPermission extends Permission {

  private static final long serialVersionUID = 4812713037565136922L;
  private static final String NAME = "ToggleSecurityManagerPermission";

  public ToggleSecurityManagerPermission() {
    super(NAME);
  }

  @Override
  public boolean implies(Permission permission) {
    return this.equals(permission);
  }

  @Override
  public boolean equals(Object obj) {
    if (obj instanceof ToggleSecurityManagerPermission) {
      return true;
    }
    return false;
  }

  @Override
  public int hashCode() {
    return NAME.hashCode();
  }

  @Override
  public String getActions() {
    return "";
  }

}

사용자 정의 SecurityManger 및 / 또는 AccessController 를 사용해야 할 것입니다 . 자세한 내용 은 Sun의 Java Security Architecture기타 보안 설명서참조하십시오 .

참고 URL : https://stackoverflow.com/questions/502218/sandbox-against-malicious-code-in-a-java-application

반응형