早期JAVA的安全模型被称作”沙箱(sandbox)“,通过定义这样一个用户可配置的保护域来实现代码的安全性管理,紧接着在JDK1.4中引入了健壮的全功能安全体系,该体系仍然基于沙箱这一概念的,不过新体系的安全策略是向ProtectionDomain授权权限而不是针对单一代码段授权权限
在最初的沙箱定义中,条件过于严格,导致善意的程序运行受限,新的安全平台体系中引入了代码签名和认证的信任模式,在这种模式下,可以根据代码数字签名的可信度给与适当的执行权限,使足够可信度的代码将充分发挥其作用。
沙箱由四个基本部分组成:
- 类装载器结构
- class文件验证
- 内置与jvm的和语言本身的安全特性
- 安全管理器和java api
1.类装载器结构
jvm通过类装载器实现类的加载并同时建立不同可信度的命名空间,也叫运行时包,只有被同一类加载器所加载的类相互之间才可见,因为他们在同一命名空间,不同的类加载器会创建不同的命名空间,不同命名空间之间是不允许访问(不可见的。jvm在加载类的过程了使用双亲委托模式,既是发现需要装载某一类的时候,首先将这个任务委托给自己的上级类装载器,依次规律,一般情况下类装载任务最后将委托给启动类装载器,只有在双亲无法完成装载的情况下,才由自己完成装载。双亲委托模式首先降低了恶意代码试图利用系统对java api的信任来达到目的的可能性。设想,一个恶意类被命名为java.lang.String,试图使类加载器误以为该恶意类是java api的一部分,但双亲委托将使用启动类加载器首先寻找java.lang.String,启动类加载器的搜索范围在java api中,当在api中找到这个String类后便会进行加载,而恶意的java.lang.String将不会被加载,也就是说恶意代码无法伪造身份。若恶意代码试图将自己“混"入java API,比如,一个声明为java.lang.IamHack的类,但由于启动类装载器在api中无法找到该类,它只可能被类路径装载器或用户自定义的类装载器装载,所以与api处在不同的命名空间中,仍然无法获得足够的信任
2.class文件验证
这一过程分为四个阶段
⑴文件结构验证
sun定义的class文件具有特定的结构,以0xCAFEBABE开头标识这是一份java特定的class文件(cafe babe,听说是对barista所作贡献的感谢)。class文件的每一个组成部分都声明了其类型和长度,检验器可以以此计算出正确的class文件总长度,从而判断是否有删减或附加额外的代码
下面是一个接口的16进制标识:
- 0000000: cafe babe 0000 0032 0009 0700 0707 0008 .......2........
- 0000010: 0100 0564 6f53 7468 0100 0328 2956 0100 ...doSth...()V..
- 0000020: 0a53 6f75 7263 6546 696c 6501 0009 446f .SourceFile...Do
- 0000030: 6572 2e6a 6176 6101 0015 6575 746f 7069 er.java...eutopi
- 0000040: 612f 7365 6375 7269 7479 2f44 6f65 7201 a/security/Doer.
- 0000050: 0010 6a61 7661 2f6c 616e 672f 4f62 6a65 ..java/lang/Obje
- 0000060: 6374 0601 0001 0002 0000 0000 0001 0401 ct..............
- 0000070: 0003 0004 0000 0001 0005 0000 0002 0006 ................
- 0000080: 0a .
紧接cafe babe的是虚拟机的主次版本号的16进制标识,然后是常量池中的数据个数,常量次的数据在后侧可以看到,这里包括了类名,方法名等,另外还有字符串和特征符。接下来的对类文件中字段和方法的复杂结构表示。
⑵类型数据语义检查
这一过程需要验证class文件中个组成部分的所属实例是否与声明类型相符合,以及该类型是否符合java语言规定的特定条件,比如final类是否被子类化或是final方法是否被覆写等。另外还有java所有类继承自java.lang.Object,常量池中条目是否合法等。
⑶字节码验证
型数据语义检查并不检验class字节码,字节码的检验将在这个阶段完成。JVM使用的是堆栈体系结构,在使用指令操作数(Java为单字节指令序列)必须将其装入堆栈,JVM规范中又将其称作操作码,每一个操作码后面都跟着一个或多个操作数,为JVM执行操作码时提供额外的数据。JVM依次执行每个操作码,这在JVM内部构成执行线程,每个线程被授予一个由不同栈帧构成的JAVA栈,每一个方法调用都对应内存中的一个栈帧,其中保存了方法中的局部变量和中间结果(保存中间结果的为操作数栈)。
这期间要验证操作数栈中的数据是否合法,方法的调用过程是否使用了合法的参数,局部变量的访问是否被允许(访问必须在初始化之后),字节码验证的验证工作量是比较大的,其中还包括了跳转命令的检查等。经过字节码验证后。可以确保class文件的完整性。
⑷符号引用验证
class文件中对其引用的类,方法,变量等是以文字符号描述的,该描述保证准确定位至唯一的目标。类装载器装载一个类的时候会对其中包含的字符引用作出解析,首先是查找被引用的类,但不一定会加载,因为JVM的延迟加载会确保被引用的类在第一次使用前被加载。若找到了对应的类,则将文字描述替换为直接引用,如指针。JVM会记住这些直接引用,所遇到同样的描述,则直接替换为直接引用,从而避免重复查找。
由于JAVA动态链接的特性,可能会导致二进制不兼容,尽管在改动java文件后的重新编译过程中会检查兼容性,但考虑到在运行时加载的类文件在编译时期并不会被检查,所以需要在运行时执行二进制兼容检查。
3.JVM内置安全特性
在执行期,除了符号引用验证,JVM还会对一些内建的安全特性进行检查,大致如下:
- 类型安全的引用转化
- 结构化的内存访问(非指针算法)
- GC
- 数组边界检查
- 空引用检查(NullPoint)
强制内存的结构话访问,避免了恶意用户在了解内存分布的情况下通过指针对JVM的内部结构进行破外。当然要了解JVM的内存分布也不是易事。JVM对运行时数据空间的分配是一个黑盒过程,完全由JVM自己决定如何分配,在class中没有任何相关的信息。
字节码检查的局限就是对于那些不经过字节码检查的方法(如本地方法:native method)无法验证其安全性,所以这里采用的是对动态链接库的访问控制,对于那些足有足够可信度的代码才被允许访问本地方法。具体的实现是,由安全管理器来决定代码是否有调用动态链接库的权限,因为调用本地方法必须调用动态链接库。这样一来,不可信的代码将无法通过调用本地方法来绕过字节码检查。
4.安全管理器和java api
我们建立的java application基本上可以说是畅通无阻的,其原因是默认情况下所有JAVA API的操作都是被允许的。但
如果稍加限制一下
新建一个policy(实际上默认既是为read,这点从java.lang.Class中定义getProtectionDomain()方法中可以看出的)
java 代码
- grant codeBase "file:///D:/JavaTestCode/security/securitymanager/" {
- permission java.io.FilePermission "D:/JavaTestCode/security/securitymanager/*", "read";
- };
codeBase指定要限制的类所在目录 :URL
FilePermission 中指定了要保护的文件 :资源名称
然后在运行时以
- java -Djava.security.manager -Djava.security.policy=policyURL appName
的形式指定
(也可以通过在
java.security中以policy.url.n=policyURL的形式指定)。
java 代码
- class TrustlessWriter
- {
- public static void main(String[] args)throws Exception
- {
- File data=new File("data.txt");
- FileWriter writer=new FileWriter(data);
- writer.write("this is a test");
- writer.close();
-
- }
- }
在控制台输入:
- java -Djava.security.manager -Djava.security.policy=myaccess.policy TrustlessWriter
结果将是抛出类似java.security.AccessControlException: access denied (java.io.FilePermission data.txt write)的错误信息
这里需要注意policy的格式
policy file syntax
- grant signedBy "signer_names", codeBase "URL",
- principal principal_class_name "principal_name",
- principal principal_class_name "principal_name",
- ... {
-
- permission permission_class_name "target_name", "action",
- signedBy "signer_names";
- permission permission_class_name "target_name", "action",
- signedBy "signer_names";
- ...
- };
codeBase 需用声明协议类型,若为本地文件话,需以file:声明,FilePermission里面则直接使用目标文件名或通配符
在JAVA2平台安全体系中,访问权限被类型化为对象,也既是说访问权限在Java api中被定义,所有访问权限对象继
承自抽象类java.security.Permission;
所以也可以在代码中启动SecurityManager,并执行检查
java 代码
- class TrustlessWriter
- {
- public static void main(String[] args)throws Exception
- {
-
- FilePermission filePermission=new FilePermission("D:/JavaTestCode/security/securitymanager/data.txt","write");
- System.out.println(TrustlessWriter.class.getProtectionDomain());
- AccessController.checkPermission(filePermission);
- File data=new File("data.txt");
- Writer writer=new FileWriter(data,true);
- writer.write("hello!");
- writer.close();
- }
- }
此时可省略-Djava.security.manage,执行命令变为:
- java -Djava.security.policy=myaccess.policy TrustlessWriter
(AccessController是在JAVA2中引入的,不过考虑到前向兼容,新的平台安全体系中仍然可以使用SecurityManager
来做检查,但SecurityManager的所有check方法均调用AccessController的checkPermission)
关于代码签名和认证
新安全平台中对足够信任度的代码放宽了限制,要获得充分信任须通过代码签名来实现,若我们对某一签名团体足够 信任,比如SUN,那么具有该团体签名的代码将被给予充分信任。一个基本的思路大致为,代码发布者创建私钥/公钥对,然后利用私钥对发布代码进行签名,代码使用方在获得代码发布者提供的公钥后对代码进行验证,确认代码确为该提供者提供以及在发布后未经非法修改。这其中存在一些潜在的危险,既是公钥是否是该代码发布者提供的,恶意用户可能替换掉合法的公钥,这会导致用户将给与恶意用户发布的代码以充分信任,目前的常见做法是通过一些权威的证书机构来发布证书而不是公钥,代码发布者被证书发布机构认证合格后,可以将自己的公钥交付证书发布机构,证书发布机构再通过私钥加密该公钥,从而生成证书序列。这样替换公钥的可能性就变得微乎其微。不过世上无绝对安全之事,通过证书机构发布的证书也不是绝对安全的途径。
对代码进行签名需要将所有class文件打包入jar,然后通过jarsigner对其进行散列计算。
一个简单的示例:
1.首先将CredibleDoer.class打包至 credible.jar:
- jar cvf credible.jar eutopia/security/CredibleDoer.class
2.创建别名为credible的密钥对,将其保存在至doerkeys:
- keytool -genkeypair -alias credible -keypass douyourwant -keystore doerkeys
3.为credible.jar进行签名:
- jarsigner -keystore doerkeys -storepass 111111 -keypass douyourwant credible.jar credible
完成对一个jar文件的签名后,会在jar产生两个文件,此处为credible.SF和credible.DSA
.SF文件保存了一个所有类的列表以及了签名过程中使用的摘要算法,大致如下:
credible.sf
- Signature-Version: 1.0
- SHA1-Digest-Manifest-Main-Attributes: 9QUCJMHnILftziSD8agLig+iUN8=
- Created-By: 1.6.0 (Sun Microsystems Inc.)
- SHA1-Digest-Manifest: Z1288OdOqrP+Kstqxv0pS10Je7o=
-
- Name: eutopia/security/CredibleDoer.class
- SHA1-Digest: ODGgb1aB9Ht0iVwmfhGSU6pRneo=
DSA文件是一个二进制密匙文件。
验证完整性实验:
现在通过jarsigner -verify credible.jar对该文件进行验证。若文件未经改动,一切正常
若签名后再将TrustlessDoer.class加入credible.jar且不更新签名,那么验证结果将会给出警告:
警告:
此 jar 包含尚未进行完整性检查的未签名条目。
若是替换原有class文件,比如这里替换CredibleDoer.class,将会有类似下面的错误提示:
jarsigner: java.lang.SecurityException: SHA1 digest error for eutopia/security/CredibleDoer.class
通过 keytool -exportcert -file credible.cer -alias credible -keystore doerkeys 可以导出cer正式文件,其中保存了签名的一些基本信息以及公钥
创建密钥对的算法有DiffieHellman,DSA ,RSA, EC可以通过-sigalg <算法名>指定算法,比如:
- keytool -sigalg DSA -genkeypair -alias credible -keypass douyourwant -keystore doerkeys
参考:JavaTM Cryptography Architecture API Specification & Reference的Appendix A: Standard Names以及Appendix B: Algorithms
Related Data
深入java虚拟机第二版
Permissions in the JavaTM 2 Standard Edition Development Kit (JDK)
JavaTM Cryptography Architecture API Specification & Reference
Java 安全性架构文档
JavaTM 2 Platform Security Architecture
Default Policy Implementation and Policy File Syntax
Java 授权内幕
J2EE 探索者: 用 JAAS 和 JSSE 实现 Java 安全性
分享到:
相关推荐
If you want to build a strong foundation with the JVM and get started with popular modern programming languages, then this book is for you. It begins with a general introduction to JVM and the ...
标题中提到了JVM原理、JVM调优、JVM内存模型和JAVA并发,这些都是Java虚拟机(JVM)相关的核心概念。JVM是运行Java字节码的虚拟计算机,为Java提供了一个跨平台的环境,确保Java程序可以在不同的操作系统上运行而...
Java虚拟机(JVM)是Java程序运行的核心,它负责解释和执行字节码,为Java应用程序提供了一个跨平台的运行环境。JDK(Java Development Kit)包含了开发和运行Java程序所需的所有工具,包括JVM。当我们谈论"jdk,jvm...
Java虚拟机(JVM)是Java程序运行的基础,它是一个抽象的计算机系统,负责执行Java字节码。本文将深入探讨JVM的启动过程及其基本原理。 首先,我们需要理解JVM的基本概念。JVM是Java Virtual Machine的缩写,它是...
例如,“iload”表示加载整数到操作数栈,“anewarray”用于创建数组对象,“iand”表示执行整数位与操作等。这些指令构成了 JVM 执行 Java 字节码的基础。 ##### 2.2 JVM 的 CPU 架构 JVM 的 CPU 架构是其指令...
在这个压缩包中,"JVM图解.png"可能是对JVM内部结构的可视化表示,"JVM图解"可能是一个详细的文档,解释了JVM的工作原理,而"JVM指令手册 中文版"则提供了JVM可执行的所有指令的详细信息。下面,我们将深入探讨JVM的...
SAP JVM 8.1 64位是一个专为SAP系统设计的Java虚拟机,它基于Oracle的Java Development Kit (JDK) 进行优化,以满足SAP应用程序的特定需求。SAP JVM旨在提高性能、可靠性和安全性,同时确保与SAP产品的无缝集成。...
Java Image Processing Recipes With OpenCV and JVM 英文epub 本资源转载自网络,如有侵权,请联系上传者或csdn删除 查看此书详细信息请在美国亚马逊官网搜索此书
很久之前就一直在学习JVM,但是一直也没有好好的总结,最近终于有了空闲,将之前学习的内容整理成了一个PPT。PPT也可以在这里下载: https://github.com/hitynsun/docs/tree/master/JVM 也希望大神们可以批评指正...
JVM(Java Virtual Machine,Java虚拟机)是运行所有Java程序的假想计算机,是Java程序的运行环境,负责执行指令、管理数据、内存、寄存器等,是实现Java跨平台特性的关键部分。JVM指令手册详细记录了JVM的所有操作...
Java虚拟机(JVM)是Java程序运行的核心组件,它负责解释和执行字节码,为开发者提供了跨平台的运行环境。"jvm视频及笔记"这个资源显然是一份全面学习JVM的材料,结合了视频教程和书面笔记,帮助学习者深入理解JVM的...
【jvm-mon基于控制台的JVM监视】 `jvm-mon`是一款实用的工具,它允许开发者通过控制台界面实时监控Java虚拟机(JVM)的状态。在Java开发过程中,性能分析是至关重要的,因为良好的性能能提升用户体验,降低服务器...
46页PPT详解JVM,46页PPT详解JVM,46页PPT详解JVM,46页PPT详解JVM,46页PPT详解JVM,46页PPT详解JVM,46页PPT详解JVM,46页PPT详解JVM,46页PPT详解JVM,46页PPT详解JVM,46页PPT详解JVM,46页PPT详解JVM,46页PPT...
【狂神说JVM探究】是一份集合了多种格式的学习资料,主要涵盖了Java虚拟机(JVM)的基础知识。这份资料出自B站上的【狂神说Java】系列教程,为快速入门JVM提供了详实的笔记。以下是根据这些资源可能包含的一些关键...
### JVM必知必会知识点梳理 #### 1. JVM的定义与层次 Java虚拟机(JVM)具有多重含义: - **一套规范**:即Java虚拟机规范,定义了Java虚拟机应该具有的行为。 - **一种实现**:例如HotSpot、J9、JRockit,它们都是...
JVM 输出 GC 日志导致 JVM 卡住 JVM 输出 GC 日志导致 JVM 卡住是一个常见的问题,尤其是在高并发和高性能应用中。这个问题的根源在于 JVM 的垃圾回收机制(Garbage Collection,GC),它会在 JVM 运行时周期性地...
### 深入解析JVM:Java虚拟机的精髓与挑战 #### JVM概览与重要性 JVM,即Java Virtual Machine(Java虚拟机),是Java程序员必须掌握的核心技术之一。初学者通常从简单的“HelloWorld”程序开始,逐渐接触更复杂的...
jvm java虚拟机 调优 马士兵 笔记 让你对java虚拟机调优有初步的认识
Java Image Processing Recipes: With OpenCV and JVM English | ISBN: 1484234642 | 2018 | 379 pages | PDF
Java虚拟机(JVM)是Java编程语言的核心组成部分,它为Java程序提供了运行环境,使得Java代码能够在不同的操作系统上“一次编写,到处运行”。JVM是Java平台的一部分,负责执行字节码,管理内存,垃圾收集,以及提供...