《深入Java虚拟机学习笔记》- 第3章 安全

时间:2022-04-27 18:46:38
3.1为什么需要安全性

Java的安全模型是其多个重要结构特点之一,它使Java成为适于网络环境的技术。因为网络提供了一条攻击连人的计算机的潜在途径,因此安全性是非常重要的。Java安全模型侧重于保护终端用户免受从网络下载的、来自不可靠来源的、恶意程序(以及善意程序中的bug)的侵犯。为了达到这个目的,Java提供了一个用户可配置的“沙箱”,在沙箱中可以放置不可靠的Java程序。

例如:原来在版本1.0中的沙箱对很多不可靠Java applet的活动做了限制,包括:

对本地硬盘的读写操作。

进行任何网络连接,但不能连接到提供这个applect的源主机。

创建新的进程。

装载新的动态连接库。

一、基本沙箱
  1. 组成沙箱的基本组件有:
    • 类装载器结构;
    • class文件校验器;
    • 内置于Java虚拟机(及语言)的安全特性;
    • 安全管理器及Java API;
  2. Java的沙箱安全模型,最重要的优点之一就是这些组件中的类装载器和安全管理器是可以由用户定制的;
二、类装载器体系结构
  1. 类装载器体系结构是Java沙箱中的第一道防线;
  2. 类装载器体系结构在三个方面对Java的沙箱起作用:
    • 它防止恶意代码去干涉善意的代码,这是通过为由不同的类装载器装入的类提供不同的命名空间来实现的;在Java虚拟机中,同一个命名空间内的类可以直接进行交互,而不同的命名空间中的类甚至不能察觉彼此的存在,除非显示地提供了允许它们进行交互的机制;如下图所示,类装载器1装载了Class1和Class2,方法区中存放了Class1和Class2的类型数据,但类装载器2装载了Class1和Class3,因此方法区中存放了Class1和Class3的类型数据;此时命名空间1中的Class1与命名空间2中的Class1关联到方法区中的类型数据有两份;
      《深入Java虚拟机学习笔记》- 第3章 安全
    • 它守护了信任类库的边界,这是通过分别使用不同的类装载器装载可靠的包和不可靠的包来实现的;在版本1.2中,类装载器请求另一个类装载器来装载类型的过程被形式化,称为"双亲委派模式",如果一个类装载器的双亲类装载器有能力来装载这个类型,则这个类装载器返回这个类型,否则这个类装载器试图自己来装载这个类型。
      类装载器的双亲委派模式如下:
      《深入Java虚拟机学习笔记》- 第3章 安全
      1. 启动(Bootstrap)类装载器:启动类装载器是用本地代码实现的类装载器,它负责将 <Java_Runtime_Home>/lib 下面的类库加载到内存中。由于启动类装载器涉及到虚拟机本地实现细节,开发者无法直接获取到启动类装载器的引用,所以不允许直接通过引用进行操作。
      2. 标准扩展(Extension)类装载器:标准扩展类装载器是由 Sun 的 ExtClassLoader(sun.misc.Launcher$ExtClassLoader) 实现的。它负责将 < Java_Runtime_Home >/lib/ext 或者由系统变量 java.ext.dir 指定位置中的类库加载到内存中。开发者可以直接使用标准扩展类装载器。
      3. 类路径(ClassPath)类装载器:类路径类装载器是由 Sun 的 AppClassLoader(sun.misc.Launcher$AppClassLoader)实现的。它负责将系统类路径(CLASSPATH)中指定的类库加载到内存中。开发者可以直接使用系统类加载器。
      4. 在有双亲委派模式的情况下,启动类装载器可以抢在标准扩展类装载器之前去装载类,而标准扩展类装载器可以抢在类路径类装载器之前去装载那个类,类路径类装载器又可以抢在用户自定义类装载器之前去装载它,用这种方法,类装载器的体系结构就可以防止不可靠的代码用它们自己的版本来替代可信任的类。
    • 它将代码归入某类(称为保护域),该类确定了代码可以进行哪些操作;
三、class文件检验器
  1. 和类装载器一起,class文件检验器保证装载的class文件内容有正确的内部结构,并且这些class文件相互协调一致。
  2. class文件检验器要进行四趟独立的扫描来完成它的操作:
    • class文件的结构检查:在类被装载时进行的,它必须遵从Java的class文件的固定格式,这样它才能被正确编译成在方法区中的内部数据结构;以下三趟扫描均在方法区中数据结构上进行;
    • 类型数据的语义检查:查看方法区中的数据结构中的每个组成部分,确认每个方法描述符都是符合特定语法的、格式正确的字符串;
    • 字节码验证:对字节流进行数据流分析,这些字节流代表的是类的方法,它是由被称为操作码的单字节指令组成的序列,每一个操作码后都跟一个或多个操作数;执行字节码时,依次执行每个操作码,这就在Java虚拟机内构成了执行的线程。字节码验证确保采用任何路径在字节码流中都得到一个确定的操作码,确保操作数栈总是包含正确的数值以及正确的类型;
    • 符号引用的验证:在动态链接的过程中,如果包含在一个class文件中的符号引用被解析时,class文件检验器将进行符号引用的检查;动态链接是一个将符号引用解析为直接引用的过程。当Java虚拟机执行字节码时,如果它遇到一个操作码,这个操作码第一次使用一个指向别一个类的符号引用,那到JVM就必须解析这个符号引用。在解析时,JVM执行两个基本任务:
      1. 查找被引用的类,如果必要的话,装载它;
      2. 将符号引用替换为直接引用;
四、内置于Java虚拟机(及语言)的安全特性

Java虚拟机装载了一个类,并且对它进行了每一到第三趟的class文件检查,这些字节码就可以被运行了。除了对符号引用的检验,Java虚拟机在执行字节码时还进行了其他一些内置的安全机制的操作:

  1. 类型安全的引用转换;
  2. 结构化的内存访问;
  3. 自动垃圾收集;
  4. 数组边界检查;
  5. 空引用检查;
五、安全管理器及API
    1. Java模型的前三个部分(类装载器体系结构、class文件检验器以及Java中内置的安全特性),保持Java虚拟机的实例和它正在运行的应用程序的内部完整性,使得它们不被下载的恶意或有漏洞的代码侵犯;而安全管理器它主要是保护虚拟机的外部资源不被虚拟机内运行的恶意或有漏洞的代码侵犯;
    2. 当Java API进行任何可能不安全的操作时,它都会向安全管理器请求许可,从而强制执行自定义的安全策略;