保卫 Java 应用程序的安全沙箱机制你需要了解一下

640?wx_fmt=png

如果你经常阅读源码,你会发现 Java 的源码中到处都有类似于下面这一段代码

class File {
  // 判断一个磁盘文件是否存在
  public boolean exists() {
    SecurityManager security = System.getSecurityManager();
    if (security != null) {
      security.checkRead(path);
    }
    ...
  }
}

class Socket {
  public void connect(SocketAddress endpoint, int timeout) {
    ...
    SecurityManager security = System.getSecurityManager();
    if (security != null) {
       if (epoint.isUnresolved())
          security.checkConnect(epoint.getHostName(), port);
       else
          security.checkConnect(addr.getHostAddress(), port);
       }
    }
    ...
  }
}

class ServerSocket {
  public void bind(SocketAddress endpoint, int backlog) {
    ...
    SecurityManager security = System.getSecurityManager();
    if (security != null)
       security.checkListen(epoint.getPort());
    ...
  }
}

class System {
  public static String getenv(String name) {
    SecurityManager sm = getSecurityManager();
    if (sm != null) {
       sm.checkPermission(new RuntimePermission("getenv."+name));
    }
    return ProcessEnvironment.getenv(name);
  }
}

我们平时在本地运行 java 程序时通常都不会默认打开安全检查器,需要执行 jvm 参数才会打开

$ java -Djava.security.manager xxx
$ java -Djava.security.manager -DDjava.security.policy="${policypath}"

// 内置扩展库授权规则
// 表示 JAVA_HOME/jre/lib/ext/ 目录下的类库可以全权访问任意资源
// 包含 javax.swing.*, javax.xml.*, javax.crypto.* 等等
grant codeBase "file:${{java.ext.dirs}}/*" {
 permission java.security.AllPermission;
};

// 其它类库授权规则
grant {
 // 允许线程调用自己的 stop 方法自杀
 permission java.lang.RuntimePermission "stopThread";
 // 允许程序监听 localhost 的随机可用端口,不允许随意订制端口
 permission java.net.SocketPermission "localhost:0", "listen";
 // 限制获取系统属性,下面一系列的配置都是只允许读部分内置属性
 permission java.util.PropertyPermission "java.version", "read";
 permission java.util.PropertyPermission "java.vendor", "read";
 permission java.util.PropertyPermission "java.vendor.url", "read";
 permission java.util.PropertyPermission "java.class.version", "read";
 permission java.util.PropertyPermission "os.name", "read";
 permission java.util.PropertyPermission "os.version", "read";
 permission java.util.PropertyPermission "os.arch", "read";
 permission java.util.PropertyPermission "file.separator", "read";
 permission java.util.PropertyPermission "path.separator", "read";
 permission java.util.PropertyPermission "line.separator", "read";
 permission java.util.PropertyPermission "java.specification.version", "read";
 permission java.util.PropertyPermission "java.specification.vendor", "read";
 permission java.util.PropertyPermission "java.specification.name", "read";
 permission java.util.PropertyPermission "java.vm.specification.version", "read";
 permission java.util.PropertyPermission "java.vm.specification.vendor", "read";
 permission java.util.PropertyPermission "java.vm.specification.name", "read";
 permission java.util.PropertyPermission "java.vm.version", "read";
 permission java.util.PropertyPermission "java.vm.vendor", "read";
 permission java.util.PropertyPermission "java.vm.name", "read";
};

安全检查没有通过,那就会抛出 java.security.AccessControlException 异常。即使安全检查通过了,操作系统的权限检查仍然可能通不过,这时候又会抛出其它类型的异常。

授权规则采用白名单,依据上面的配置意味着启用默认安全策略的 JVM 将无法访问本地文件。如果需要访问本地文件,可以增加下面的规则

permission java.io.FilePermission "/etc/passwd", "read";
permission java.io.FilePermission "/etc/shadow", "read,write";
permission java.io.FilePermission "/xyz", "read,write,delete";
// 允许读所有文件
permission java.io.FilePermission "*", "read";

public FilePermission(String path, String actions) {
  super(path);
  init(getMask(actions));
}

640?wx_fmt=png

其中 AllPermission 表示打开所有权限。还有一个不速之客 HibernatePermission,它并不是内置的权限模块,它是 Hibernate 框架为自己订制的,这意味着安全规则是支持自定义扩展的。扩展也很简单,可以自己编写一个 Permission 子类,实现它的 4 个抽象方法。

abstract class Permission {
  // 权限名称,对于文件来说就是文件名,对于套接字来说就是套接字地址
  // 它的意义是子类可定制的
  private String name;
  // 当前权限对象是否隐含了 other 权限
  // 比如 AllPermission 的这个方法总是返回 true
  public abstract boolean implies(Permission other);
  // equals 和 hashcode 用于权限比较
  public abstract boolean equals(Object obj);
  public abstract int hashCode();
  // 权限选项 read,write,xxx
  public abstract String getActions();
}

class CustomPermission extends Permission {
  private String actions;
  CustomPermission(string name, string actions) {
    super(name)
    this.actions = actions;
  }
  ...
}

class CustomAPI {
  public void someMethod() {
    SecurityManager sec = System.getSecurityManager();
    if(sec != null) {
      sec.CheckPermission(new CustomPermission("xname", "xactions"));
    }
    ...
  }
}

沙箱的安全检查点非常多,下面列举一些常见的场景

  1. 文件操作

  2. 套接字操作

  3. 线程和线程组

  4. 类加载器控制

  5. 反射控制

  6. 线程堆栈信息获取

  7. 网络代理控制

  8. Cookie 读写控制

640?wx_fmt=png

阅读更多精品文章,长按图片识别二维码关注「码洞」

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值