java如何实现安全性

原文地址:Inside The JVM Part2: java如何实现安全性

Java通过提供一个”安全沙箱“来保证从网络或者其他不信任的地方下载并运行的程序不会破坏本地数据,为了确保沙箱是可靠的,java安全模型对体系结构的各方面都进行了考虑。组成java沙箱的基本组件如下:

  • 类装载器结构
  • class文件检验器
  • 内置于Java虚拟机(及语言)的安全特性
  • 安全管理器及Java API

Java的沙箱安全模型,最重要的优点之一就是这些组件中的类装载器和安全管理器是可以由用户定制的。

1、类装载器体系结构
java沙箱中,类装载器体系结构是第一道防线,类装载器体系结构在三个方面对Java的沙箱起作用:

  • 它防止恶意代码去干涉善意的代码。-------通过为由不同的类装载器装入的类提供不同的命名空间来实现的,这个命名空间由Java虚拟机维护
  • 它守护了被信任的类库的边界。--------通过分别使用不同的类装载器装载可靠的包和不可靠的包来实现,参见例1和例2的加深理解。
  • 它将代码归入了某类(称为保护域),该类确定了代码可以进行哪些操作。

例1:如果某个恶意的类可以成功欺骗Java虚拟机,使Java虚拟机相信它是一个来自Java API的可信任类,那么,这个恶意的类就可以突破沙箱的阻隔了,让我们看看java如何阻止这种情况的发生。
Java的类装载器结构是一个以启动类装载器为根的委派链,子类装载器在装载一个类时首先会请求其双亲类装载器来装载,如果双亲装载器能够装载成功,则直接使用该类型,只有所有双亲类装载器都装载失败的时候,才会根据自定义的方法去装载该类型,这中工作方式被称作”双亲委派模式“。
在这种模式下,如果一个自定义的网络类装载器试图从网络上下载一个和Java API中某个类型同名的类型,如java.lang.Integer时,它将不能成功,因为通过层层委派,这个类型会被启动类装载器装载,而网络类装载器将直接使用正确的java.lang.Integer,而没有机会从网络上下载并装载这个恶意的java.lang.Integer。

例2:如果自定义的网络类装载器不像例1一样去替换一个被信任的类,而是在被信任的包中插入一个全新的类型,比如,一个java.lang.Virus的时候,将会发生什么?
通过层层委派,网络类装载器最终会正确装载这个名为java.lang.Virus的类,暗示了这个类是Java API的一部分,因此,它可以访问java.lang包中被信任类的特殊访问权限(protected),然而,这个情况不会发生,因为java虚拟机只把彼此访问的特殊权限授予由同一个类装载器装载到同一个包中的类型,即只有同一个”运行时包“的类之间才有特殊访问权限,而 java.lang.Virus和java.lang中其他被信任的类分别有网络类装载器和启动类装载器装载,它们不属于同一个运行时包。

2、class文件检验器
和类装载器一起,class文件检验器包装装载的class文件内容有正确的内部结构,并且这些class文件相互间协调一致,class文件检验器实现的安全目标之一就是程序的健壮性,它必须保证一个class的装载不会导致虚拟机的崩溃。
class文件检验器要进行四趟独立的扫描来完成它的操作。第一趟扫描是在类被装载时进行的,在这次扫描中,class文件检验器检查这个class文件的内部结构,以保证它可以被安全的编译;第二趟和第三趟扫描是在连接过程中进行的,在这两次扫描中,class文件检验器确认类型数据遵从Java编程语言的语义,包括检验它所包含的所有字节码的完整性;第四趟扫描是在进行动态连接的过程中解析符号引用时进行的,在这次扫描中,class文件检验器确认被引用的类、字段以及方法确实存在。

第一趟扫描: class文件的结构检查
class文件检验器会检查每一段被当作类型导入的字节序列是否符合java class文件的基本结构,比如是否是以魔数0xCAFEBABE开头,确认class文件中声明的主版本号和次版本号是否在这个java虚拟机的支持范围内,必须确认这个文件没有被删节或者附加一些字节(通过每个定义长度的地方来确定总体长度)。第一趟扫描的主要目的是保证这个字节序列正确的定义了一个类型,它必须遵从java class文件的固定格式,这样它才能被编译成在方法区中的(基于实现的)内部数据结构。第二、三、四趟扫描不是在符合class文件的二进制数据上进行的,而是在方法区中、由实现决定的数据结构上进行的。

第二趟扫描: 类型数据的语义检查
这次检查,class文件检验器不需要查看字节码,也不需要查看和装载任何其他类型。在这趟扫描中,检验器查看每个组成部分,确认它们是否是其所属类型的实例,它们结构是否正确。比如,方法描述符(它的返回类型,以及参数的类型和个数)在class文件中被存储为一个字符串,这个字符串必须符合特定的上下文无关文法。另外,还会检查这个类本身是否符合特定的条件,它们是由java编程语言规定的。比如,除Object外,所有类都必须要有一个超类,final的类不能被子类化,final方法也没有被覆盖,检查常量池中的条目是合法的,而且常量池的所有索引必须指向正确类型的常量池条目。

第三趟扫描: 字节码验证
字节码流代表了java的方法,它是由被称为操作码的单字节指令组成的序列,每一个操作码后都跟着一个或多个操作数。执行字节码时,依次执行操作码,这就在java虚拟机内构成了执行的线程,每一个线程被授予自己的java栈,这个栈是由不同的栈帧构成的,每一个方法调用将获得一个自己的栈帧----栈帧其实就是一个内存片段,其中存储着局部变量和计算的中间结果,用于存储中间结果的部分被称为操作数栈。
字节码检验器要进行大量的检查,以确保采用任何路径在字节码流中都得到一个确定的操作码,确保操作数栈总是包含正确的数值以及正确的类型。它必须保证局部变量在赋予合适的值以前不能被访问,而且类的字段中必须总是被赋予正确类型的值,类的方法被调用时总是传递正确数值和类型的参数。字节码检验器还必须保证每个操作码都是合法的,即都有合法的操作数,以及对每一个操作码,合适类型的数值位于局部变量中或是在操作数栈中。这些仅仅是字节码检验器所做的大量检验工作中的一小部分,在整个检验过程通过后,它就能保证这个字节码流可以被java虚拟机安全的执行。

第四趟扫描: 符合引用的验证
在动态连接过程中,如果包含在一个class文件中的符号引用被解析时,class文件检验器进行第四趟扫描。在这趟扫描中,java虚拟机将追踪那些引用-----从被验证的class文件到被引用的class文件,以确保这个引用是正确的。这次扫描可能要装载新的类。考虑到虚拟机实现上的差别,第四趟扫描可能紧随第三趟扫描发生,也有可能在第三趟扫描之后很久,当字节码被执行时才执行。
动态连接是一个将符号引用解析为直接引用的过程。当java虚拟机执行字节码时,如果它遇到一个操作码,这个操作码第一次使用一个指向另一个类的符号引用,那么虚拟机就必须解析这个符号引用。在解析时,虚拟机执行两个基本任务:
1)查找被引用的类(如果必要的话就装载它)
2)将符号引用替换为直接引用,例如指向一个类、字段或方法的指针或偏移量
虚拟机必须记住这个直接引用,这样当它以后再次遇到同样的引用时,就可以直接使用,而不需要重新解析该符号引用了。

二进制兼容性规则
为了能方便的修改类库的代码,java编程语言被设计成允许对一个类做多种修改,但并不要求对依赖于它的那些类进行重编译。java语言规范中列出了用户可以做的多种改动,这些改动称为二进制兼容性规则。这些规则明确地定义了:在一个类中,哪些可以被修改、增加和删除,而并不破坏这个被修改的类与依赖于它的那些事先已经存在的类之间的二进制兼容性。

3、java虚拟机中内置的安全特性
除了四趟扫描之外,java虚拟机在执行字节码时还进行其他一些内置的安全机制的操作,这些机制大多数是java的类型安全的基础,它们作为java编程语言保证java程序的健壮性,同样,它们也是java虚拟机的特性:

  • 类型安全的引用转换
  • 结构化的内存访问(无指针算法)
  • 自动垃圾收集(不必显式地释放被分配的内存)
  • 数组边界检查
  • 空引用检查

4、安全管理器和Java API
java安全模型的前三个部分共同达到了一个目的:保持java虚拟机的实例和它正在运行的应用程序的内部完整性,使得它们不被下载的恶意或有漏洞的代码侵犯。相反,java安全模型的第四个组成部分---安全管理器---则用于保护虚拟机外部资源不被虚拟机内运行的恶意或有漏洞的代码侵犯。这个安全管理器是一个单独的对象,在运行的java虚拟机中,它在访问控制---对于外部资源的访问控制---中起中枢作用。
安全管理器定义了沙箱的外部边界,并且它是可以定制的。
安全管理器中需要了解如下一些概念和类:

  • 默认安全管理器:java.lang.SecurityManager
  • 代码签名和认证
  • 策略:java.security.Policy
  • 权限:java.security.Permission
  • 策略文件
  • 保护域:CodeSource,PersimissionCollection,ProtectionDomain
  • 访问控制器:java.security.AccessController

这部分内容太多,只能带过了。

Java安全模型的不足
Java安全模型尚不能解决如下问题:

  • 不断分配内存,直到内存耗尽
  • 不断生成线程导致每件事都慢的不可忍受

以上两中类型的攻击被称作拒绝服务攻击(DOS)
另一个没有放入安全模型的领域是关于将权限映射到系统用户,代码以这个用户的名义来运行。这中访问控制在unix系统中较为常见,它基于用户ID对文件的访问进行控制

你可能感兴趣的:(web)