一尘不染

针对Java应用程序中恶意代码的沙盒

java

在允许用户提交自己的代码以由服务器运行的模拟服务器环境中,将任何用户提交的代码在沙箱中运行显然是有利的,这与浏览器中的Applet不同。我希望能够利用JVM本身,而不是添加另一个VM层来隔离这些提交的组件。

使用现有的Java沙箱模型似乎可以实现这种限制,但是是否有一种动态的方法可以仅对正在运行的应用程序的用户提交的部分启用此限制?


阅读 271

收藏
2020-03-15

共1个答案

一尘不染

  1. 在自己的线程中运行不受信任的代码。例如,这可以防止无限循环等问题,并使以后的步骤变得更容易。让主线程等待线程完成,如果花费的时间太长,请使用Thread.stop将其杀死。Thread.stop已过时,但由于不受信任的代码不应访问任何资源,因此可以安全地将其杀死。

  2. 在该线程上设置一个SecurityManager。创建一个SecurityManager的子类,该子类重写checkPermission(Permission perm)以简单地为除少数几个之外的所有权限抛出SecurityException。这里列出了方法及其所需的权限:Java TM 6 SDK 中的权限。

  3. 使用自定义的ClassLoader加载不受信任的代码。你的类加载器将被调用所有不受信任代码使用的类,因此你可以执行诸如禁用对单个JDK类的访问之类的操作。要做的事情是拥有允许的JDK类的白名单。

  4. 你可能要在单独的JVM中运行不受信任的代码。尽管前面的步骤可以确保代码安全,但是孤立的代码仍然可以做一件令人讨厌的事情:分配尽可能多的内存,这将导致主应用程序的可见内存增加。

JSR 121:“应用程序隔离API规范”旨在解决此问题,但不幸的是它尚未实现。

这是一个非常详细的主题,而我大部分时间都是在写这些。

但是无论如何,一些不完善的,自己动手使用的风险,可能是错误的(伪)代码:

类加载器

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) {}
  }
}
2020-03-15