(转载) 深入JVM学习笔记-安全性

此处附上原文地址 http://blog.csdn.net/mrliu20082009/article/details/7329742

1、 基本沙箱

(1)      类加载器

a) 它防止恶意代码干涉善意代码。为由不同的类加载器加载的代码提供不同的命名空间,

在java虚拟机中,在同一个命名空间的类可以直接交互,但是不同命名空间的类根本无法

知道对方的存在,当然也可以通过显示地提供允许它们交互的,下面的例子就是一种显示

访问另一个命名空间的方式:

[java] view plaincopy

  1. /*LoaderSample2.java*/
  2. import java.net.*;
  3. import java.lang.reflect.*;
  4. public class LoaderSample2 {
  5. public static void main(String[] args) {
  6. try {
  7. String path = System.getProperty("user.dir");
  8. URL[] us = {new URL("file://" + path + "/sub/")};
  9. ClassLoader loader = new URLClassLoader(us);
  10. Class c = loader.loadClass("LoaderSample3");
  11. Object o = c.newInstance();
  12. Field f = c.getField("age");
  13. int age = f.getInt(o);
  14. System.out.println("age is " + age);
  15. } catch (Exception e) {
  16. e.printStackTrace();
  17. }
  18. }
  19. }
  20. /*sub/Loadersample3.java*/
  21. public class LoaderSample3 {
  22. static {
  23. System.out.println("LoaderSample3 loaded");
  24. }
  25. public int age = 30;
  26. }
  27. 编译:
  28. javac LoaderSample2.java;
  29. javac sub/LoaderSample3.java
  30. 运行:java LoaderSample2
  31. LoaderSample3 loaded
  32. age is 30

下图也直接说明了不同命名空间的类的装载方式:

两个类加载器虽然加载的Volcano类型一样,但是在方法区中却不一样。

b) 类装载器的体系结构守护了被信任的类库的边界。通过分别使用不同的类加载器加载

可靠和不可靠的包实现,它会剔除装作被信任的不可靠类。在双亲委派机制下,启动类加载

器可以抢在标准扩展类加载器之前去装载类(这样java核心类就不可替代),而标准扩张类

加载器可以抢在类路径加载器之前去装载那个类,类路径加载器又可以抢在网络类加载器之

前去加载它。如果网络类加载器试图从网络加载一个Java API中的某个类如

java.lang.Integer,因为该类已经在java API核心包中存在,启动类加载器会从核心包中加载

java.lang.Integer,而不会从网络中加载java.lang.Integer;再比如网络类加载器试图加载一个

新的类java.lang.Virus,因为Java允许在同一个包中的类具有彼此访问的特殊权限,暗示着

java.lang.Virus是java api的一部分,但虚拟机需要确认他们是不是同一个运行时包,即由同

一个类加载器加载,因为javaapi中的类由启动类加载器加载,而java.lang.Virus是有网络

类加载器加载,他们依然不能彼此访问。

c) 类装载器会加每一个加载类都放置在一个保护域中,它定义了每个类在运行时的权限。

(2)      Class文件校验器

它保证了类装载器装载的class文件是具备正确的内部结构,并且这些class文件相互间

协调一致。它总共有4趟校验步骤:

a) 第一趟:class文件的结构检查。它发生在类被装载时,会校验文件是不是以四个同样的

字节开始:魔数0xCAFEBABE;校验声明的主版本号和次版本号是否在java虚拟机可以

支持的范围内;有没有被删节,文件默认有没有被追加字节,因为文件的每个组成部分都声

明了它的长度和类型,校验器可以通过组成部分声明的类型和长度来计算文件的总长度。等等。。。它的校验目的就是保证这个字节序列正确定义了一个新类型,它必须遵循java的class

文件的固定格式。

b) 第二趟:类型数据的语义检查,它校验类文件中的每个组成部分,确认它们是否是所属

类型的实例,它们的结构是否正确,是否符合Java语言应该在编译时遵守的强制规则。如

除了Object类以外其他所有的类是否都有超类,final类有没有被子类化,final方法有没有

被覆盖等。

c) 第三趟:字节码验证,确保采用任何路径在字节码流中都得到一个确定的操作码,操作

数栈总是包含正确的数值以及类型等等,总之通过字节码的验证保证这个字节码流在java

虚拟机中能被安全地执行。

d) 第四趟:符号引用的验证,在动态链接过程汇总,如果一个符号引用被解析,将会启动

符号应用的验证,它会验证它所有引用的类是否存在。大多数虚拟机的实现采用延迟加载

的策略,所以在预先装载时不会包NoClassDefFoundError错误,而是直到这个引用类首次

被程序使用时才抛出。这样第四趟可能发生在第三趟之后,也可能发生在当字节码被执行

的时候。除了校验引用是否存在,还是校验引用的类版本是否兼容。

(3)      内置的安全特性

a) 类型安全的引用转换

b) 结构化的内存访问(无指针算法)

使用类型安全的、结构化的方法访问内存,使得java程序更健壮。同时java虚拟机也不指

明运行时数据空间在java虚拟机内部是怎么分布的,在class文件内部查到不到任何内存地

址,当然这种限制可以通过本地方法来突破,而安全管理器提供了一个方法来确定一个程序

是否可以状态动态链接库,因为调用本地方法是动态链接库是必需的。

c) 自动垃圾收集

d) 数组越界检查

e) 空引用检查

f)  异常的结构化处理:

一个异常抛出不会导致整个系统崩溃,而是某几个线程死亡。

(4)       安全管理器和Java API

它的作用和之前的3个恰好相反,它主要用于保护虚拟机的外部资源不被虚拟机内运行的

恶意或有漏洞的代码侵犯。

当java程序启动时,它还没有安全管理器,但是应用程序通过将一个指向

java.lang.SecurityManager或是其子类的实例传给setSecurityManager(),以此来安装安全管理

器,它不是必选的。如果在应用程序中安装了安全管理器,在1.0/1.1版本中是不可更改的,

在1.2版本中通过调用System.setSecurityManager()实现。

如果一个程序安装了安全管理器,当Java API执行操作时,如下图调用所示:

2、 代码签名和认证

认证的作用是可以是用户确认,由某些团体担保的一些class文件是值得信任的,并且这些

class文件在到达用户虚拟机的途中没有被改变过。下图是一个签名的流程图:

散列普遍采用64或128为,产生重复的概率理论是2的64或128次幂才可能产生一个重复

的散列值,所以要黑客要想在没有私钥的情况下,产生相同的散列值来破解签名是很困难的。

下图是一个对签名文件进行验证的流程:

虽然认证技术使用了可信赖的数学原理,但数学不能解决所有问题,如它没有说明该信任谁,

以及该信任到什么程度。而且认证技术是建立在私钥是被秘密保管的前提之下,一旦私钥

丢失,不仅无效,还且还带来危险,因为它给出了一个错误的安全意义。同时,公钥的发布

也是一个问题,因为公钥是公开的,有公钥的人不用担心公钥被别人拿走,但是拿公钥的

人就需要担心自己拿到的公钥是不是来自自己想要得到公钥的个人或者团体,比如你想

从A那里拿公钥,可能拿到B的公钥(B利用某些非法手段替换了A的公钥),这时B就

可以利用你对A的签名信任来侵入你的系统。为了解决公钥的问题,建立了许多证书机构

来为这些公钥做担保,如A可以到一个证书机构给出他的一些信任证明以及他的公钥,然

后证书机构验证OK之后利用自己的私钥对A的公钥进行签名,最后得到的数字序列被称

为证书,A就可以不公布公钥,而只公布证书,当你得到证书之后,可以用证书机构的公钥

对证书进行解密得到公钥,然后使用公钥解密文件签名。B要想替换A的公钥,还必须知

道证书机构的私钥,要困难很多了。当然,这样证书机构又成为了安全瓶颈,但这已经大大

降低了安全风险,安全性是一种代价和安全之间的折中:安全风险越小,安全的代价越高。

3、 其他

(1)      策略

一个应用程序只会有一个Policy对象,可以通过Policy.setPolicy方法在运行时修改。

每个Policy对象包含了CodeSource与PermissionCollection的映射,它记录了每个装载的

类型具备哪些权限。在一个类加载器装载类时,会创建CodeSource实例,初始化PermissionCollection,同时利用这两个对象创建ProtectDomain,然后调用defineClass方法

将类装载到jvm中,这也就是说类加载器决定了哪些类型具备哪些权限,可以在装载的时

候增加或删减已定义的权限或者直接不进行权限验证。要启用策略需要启动安全管理器同时

指定策略文件:

java -Djava.security.manger-Djava.security.policy=policy.txt

一定策略文件定义的例子:

keystore "ijvmkeys";

grant signBy "friend" {

permissionjava.io.FilePermission "question.txt","read";

}

grant signBy "stranger" {

permissionjava.io.FilePermission "question.txt","write";

}

grant codeBase "file:${com.nearme.tools.security.*}"{

permissionjava.io.FilePermission "answer.txt","read";

permissionjava.io.FilePermission "answer.txt","write";

}

第一行指定了签名文件的文件名,2-4行指定了friend签名的代码对question.txt具备读权限,

5-7行指定了stranger签名的代码对question.txt具备写权限,8-11行指定

com.nearme.tools.security代码库下面的代码对answer.txt有读写权限。

(2)      保护域

每一个类型都有一个保护域,在类装载的时候指定。下图说明了保护域在权限验证方面

所处的地位。

当一个类加载器吧Friend和Friend$1导入方法区时,将把创建好的ProtectionDomain实例

引用和这些class文件的字节传递给defineClass()方法,这个defineClass()方法将Friend和

Friend$1所在的方法区中的类型数据和被传递的ProtectionDomain对象相关联。

(3)      访问控制器

AccessController提供了一个默认的安全策略执行机制,他使用栈检查来决定潜在不安全的

操作是否被允许。它的checkPermission实现的基本算法决定了调用栈中的每一帧是否有权

执行潜在不安全的操作。

时间: 2024-10-17 01:28:12

(转载) 深入JVM学习笔记-安全性的相关文章

java之jvm学习笔记六-十二(实践写自己的安全管理器)(jar包的代码认证和签名) (实践对jar包的代码签名) (策略文件)(策略和保护域) (访问控制器) (访问控制器的栈校验机制) (jvm基本结构)

java之jvm学习笔记六(实践写自己的安全管理器) 安全管理器SecurityManager里设计的内容实在是非常的庞大,它的核心方法就是checkPerssiom这个方法里又调用 AccessController的checkPerssiom方法,访问控制器AccessController的栈检查机制又遍历整个 PerssiomCollection来判断具体拥有什么权限一旦发现栈中一个权限不允许的时候抛出异常否则简单的返回,这个过程实际上比我的描述要复杂 得多,这里我只是简单的一句带过,因为这

JVM学习笔记(四)------内存调优【转】

转自:http://blog.csdn.net/cutesource/article/details/5907418 版权声明:本文为博主原创文章,未经博主允许不得转载. 首先需要注意的是在对JVM内存调优的时候不能只看操作系统级别Java进程所占用的内存,这个数值不能准确的反应堆内存的真实占用情况,因为GC过后这个值是不会变化的,因此内存调优的时候要更多地使用JDK提供的内存查看工具,比如JConsole和Java VisualVM. 对JVM内存的系统级的调优主要的目的是减少GC的频率和Fu

JVM学习笔记(一)------基本结构

从Java平台的逻辑结构上来看,我们能够从下图来了解JVM: 从上图能清晰看到Java平台包括的各个逻辑模块,也能了解到JDK与JRE的差别 对于JVM自身的物理结构,我们能够从下图俯视一下: 对于JVM的学习,在我看来这么几个部分最重要: Java代码编译和运行的整个过程 JVM内存管理及垃圾回收机制 以下将这两个部分进行具体学习 JVM学习笔记(一)------基本结构,布布扣,bubuko.com

JVM 学习笔记

JVM  ----Java  Virtual Machine   (熟称:JAVA虚拟机),JVM 在执行JAVA程序的过程中将内容划分为若干个区域,其有各自的用途和管理机制.如下图: 1.  程序计算器(Program Counter Register)  --  是当前线程所执行字节码的行号指示器,通过改变其值来实现执行不同的代码指令.内存占用小,线程私有,支持多线程处理(多线程时,每个线程有一个独立程序计算器,已达到各自互不影响), 也正是由于这些特点,该区域是JVM规范中唯一没有规定任何

java之jvm学习笔记十三(jvm基本结构)

java之jvm学习笔记十三(jvm基本结构) 这一节,主要来学习jvm的基本结构,也就是概述.说是概述,内容很多,而且概念量也很大,不过关于概念方面,你不用担心,我完全有信心,让概念在你的脑子里变成图形,所以只要你有耐心,仔细,认真,并发挥你的想象力,这一章之后你会充满自信.当然,不是说看完本章,就对jvm了解了,jvm要学习的知识实在是非常的多.在你看完本节之后,后续我们还会来学jvm的细节,但是如果你在学习完本节的前提下去学习,再学习其他jvm的细节会事半功倍. 为了让你每一个知识点都有迹

java之jvm学习笔记五(实践写自己的类装载器)

java之jvm学习笔记五(实践写自己的类装载器) 课程源码:http://download.csdn.net/detail/yfqnihao/4866501 前面第三和第四节我们一直在强调一句话,类装载器和安全管理器是可以被动态扩展的,或者说,他们是可以由用户自己定制的,今天我们就是动手试试,怎么做这部分的实践,当然,在阅读本篇之前,至少要阅读过笔记三. 下面我们先来动态扩展一个类装载器,当然这只是一个比较小的demo,旨在让大家有个比较形象的概念. 第一步,首先定义自己的类装载器,从Clas

JVM学习笔记-内存管理

第一章 内存分配 1. 内存区域. 方法区和堆(线程共享),程序计数器 , VM栈 和 本地方法栈(线程隔离). 1) java虚拟机栈:线程私有.描述的是java方法执行的内存模型:栈帧,用户存储 局部变量表,操作数栈,动态链接,方法出口等信息. 局部变量表在编译时即可完全确定!如果线程请求的栈深度大于 规定的深度,StackOverflowError. 2) 本地方法栈,类似. 3)堆:垃圾收集器管理的主要区域.线程共享. 4)方法区: 各个线程共享.存储:加载的类信息,常量,静态变量,即时

JVM学习笔记:虚拟机的类加载机制

JVM类加载机制分两部分来总结: (1)类加载过程 (2)类加载器 一.JVM类加载过程 类的加载过程:加载 →连接(验证 → 准备 → 解析)→ 初始化. 类的生命周期:加载 →连接(验证 → 准备 → 解析)→ 初始化 → 使用 →  卸载. 1 加载 1.1 加载阶段要做的3件事情 通过一个类的全限定名来获取该类对应的二进制字节流. 将这个字节流所代表的静态存储结构转化为方法区的数据结构. 在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口

JVM 学习笔记(二)

JVM 堆中几乎存放着java中所有的对象实例,在在垃圾回收前先要判断对象是否已死,这里对对象的判断主要有: 1.  引用计数法 给对象中添加一个引用计数器,每当有一个地方引用他时,计数器就加1:当引用失效时,计数器就减1.任何时间计数器为0时,对象就不可能在次使用. 2.  根搜索法 通过一系列的名为GC Roots 作为起点,从这些节点开始向下搜索,搜索走过的路径成为引用链(Reference Chain), 当一个对象到GC Roots 没有任何引用链时,则说明此对象时不可到达的. 可以作