java security model是Java的一个重要的架构特征,这个架构特征保证了Java可以作为网路环境程序开发的技术。因为在网络环境下的所有程序security安全是个重要因素。我们需要保证网络环境下下载的程序本地运行时安全的。

Java security model侧重于保护终端用户在运行从网络上下载的恶意程序时以免中招。实现这个目的就是通过可定制话的sandbox来实现的。一个java程序必须在这个sandbox中运行,这个sandbox可以禁止很多不安全的操作。哪些操作是不安全的或会引起潜在危害的?

  1.  对本地文件读写操作。
  2. 网络连接
  3. 创建进程或更改进程优先级
  4. 直接调用本地方法或动态库。

Java的沙盒模式允许来自任何源的代码,但是如果代码来自未授信的来源时,沙盒就会约束这些代码做任何有害你的系统的操作和行为。Java sandbox由四大护法来保证安全性:

  1.  class loader architecture 类加载架构机制
  2. the class file verifier 类文件检查器
  3. safety features builts into the JVM (JVM和Java语言内置的一些安全特性)
  4. the security manager and the Java API

这四大护法中class loader和security manager提供了可定制性,从而我们可以根据我们java应用程序自身需求来定制security policy安全方针。

类加载架构(The class loader architecture )

它保证了授信类库的边界以及防止恶意代码与正常代码的交互。

为了防止恶意代码伪装成信任的代码(如写一个java.lang.MaliciousClass伪装成java.lang包下的一个类) 类加载架构机制为类提供了保护的name-space。每个加载的类的单独名字的结合构成了一个命名空间。这个name-space由JVM维护。一旦一个类被加载到一个特定的name-space,那在这个name-space中就不可以有另一个具有相同名称的类。

name-space就给加载到不同的name-space的类提供了一个防护盾;在同一个name space中的类可以直接交互;在不同的命名空间的类如果不明确告知的话甚至连对方是否存在都不清楚。我想这就是包访问的原理。

class loader的加载机制是parent delegate机制,即先有parent classloader去加载,如果找不到的话,当前class loader才去尝试加载。都加载不到就会抛class not found(ClassNotFoundException)异常。

每个自定义的class loader都会依赖于其他class loader;至少会依赖于原始的class loader.

  1. bootstrap class loader (引导类加载器:用来加载java的核心库)
  2. extension class loader (扩展类加载器:用来加载java的lib/ext目录下的扩展库)
  3. system class loader (系统类加载器:根据java应用的classpath来加载类)

当JVM第一次执行某个class的方法时,会要求该类的classloader去加载该方法中使用到的引用类,这个过程也是parent delegate的过程,即该类classloader会交由parent class loader去先尝试加载。

我们在写classloader时要保证那些要被自己加载的类不能声明为一个授信包的成员。

你可能已经在授信任的库中安装了一些包,这些包包含一些类;这些类你希望应用程序能够通过原始的class loader来加载,同时不希望它们被你的类加载器加载的类访问。对于这些重要的特殊类(可能包含重要资源和重要权限),我们需要在自定义的classloader中给予甄别和拦截,应该抛安全异常,也不要交由原始classloader去加载。这样的包可以称为禁止访问包(forbidden packages)。例如名为absolutePower的包是个禁止访问包,那么自定义的classloader就不应该去加载该package下的任何类如absolutePower.xxxClass,而是直接抛安全异常。

通常编写一个安全的类加载器需要使用下面四步:

  1. 如果存在该classloader不允许加载的包,即forbidden packages,该类加载器首先要核对是否请求的类是属于任何forbidden packages。如果是的话就抛安全异常;不是的话就继续下一步。
  2. 转发请求给原始类加载器;如果原始加载器加载成功,那就return该类,否则就进行下一步。
  3. 判断是否存在该类加载器不允许添加新的类进入的信任包;如java.lang;不应该将一个加载的未信任的类放入一个已经标为信任的包;如果是的话就抛安全异常;若果不是的话就进行下一步。
  4. 最后该类加载器尝试通过自定义的方法来加载该类,如从网络上下载。如果成功就返回该类,失败就会抛no class definition found错误。

Class文件检查器(The Class File Verifer)

类文件检查器检查加载的类是否是满足一个java class内部结构。如果该文件结构不合法就会抛异常。它保证了程序的健壮性。一个类文件检查器执行检验有两个阶段。第一个阶段发生在类刚被加载后,第二个阶段发生在字节码执行的时候。

阶段一:(加载后)文件本身内部检查

这一阶段只是针对文件本身做内部检查,文件结构检查,比如文件格式,文件头(0xCAFEBABE),文件是否被截断或者被加了尾巴。要保证bytecodes的一致性。语法检查,比如每个部分是否well-formed(如方法描述符应包含返回类型,参数的个数以及类型)。比如类本身是否符号java语言的一些约束条件(如类的单继承)。之后还要验证bytecodes,即字节码检查; 这个过程会涉及到一些术语,如opcodes, operands, Java Stack, frames, operand stack。这里不多讲了,感兴趣可以去研究一下。

阶段二:(执行时)符号引用的检验

符号引用检查该类引用的类文件,确保这些引用是正确的。这是将符号引用转化为直接引用的过程。当一个类被加载的过程中,它包含了对其他类的符号引用以及它们的方法和字段。符号引用只是一个字符串,包含一些名称,描述信息,如类名称,方法名称等。这是一个动态链接的过程。

Java语言的内置安全特性

JVM和Java语言内置了一些安全特性,从而加强了程序的健壮性。如

  1. null check空值检查
  2. type-safe reference casting 安全的引用类型转化
  3. structured memory access (no pointer arithmetic) 结构化内存访问
  4. 自动的GC
  5. 数组越界检查

Security Manager and the Java API

通过使用类加载器可以阻止不同类加载器加载的类代码在JVM中相互干扰,但却不能保护对JVM以外的资产,如本地文件,本地进程等。这时候就需要使用Security Manager。它定义了sandbox的外边界。Java API在其执行任何可能有安全问题的操作前首先需要征求Security Manager的允许。Java API实施自定义的security policy。每个不安全的动作在security manager中都有一个方法,定义了是否在sandbox中允许执行该动作。每个方法名称都以check开头,比如说checkRead(), checkWrite()。通常要check的活动有:

  1. socket相关的,在特定主机和端口接受socket connection,或等待连接,或打开一个连接。
  2. 线程或进程相关的,如修改一个线程(更改优先级,停止),如创建一个新进程。
  3. 类加载相关的,如创建一个新的类加载器,从特定包中加载一个类,向一个特定包中加入一个新类。
  4. 导致application退出的操作。
  5. 加载包含本地方法的动态库。
  6. 系统属性相关的操作,如访问或修改系统属性(system properties)、
  7. 文件操作,读写特定的文件。

以上这些操作都会有些安全风险。

当启动一个java应用时,它并没有security manager。 但是application可以在其option选项中安装一个security manager。如果没有安装security manager,那么就对java API执行任何活动时没有约束。

尽管应用程序只可以安装一个security manager,但是我们可以自己写security manager来建立多个security policy。

发表评论