微型机与应用
MICROCOMPUTER & ITS APPLICATIONS
1999年 第18卷 第1期 Vol.18 No.1 1999



JAVA的安全结构
冀振燕　程虎
　　摘　要：Java的安全结构——“沙盒”模型的4个组成部分：内建于Java虚拟机和语言的安全特性、类的载入结构、类文件校验器、安全管理器和JAVA API。
　　关键词：Java语言　安全性　沙盒结构
　　JAVA的安全模型不同于传统的安全方法，传统的安全方法中，大多数操作系统允许应用程序充分访问系统资源，在操作系统不提供安全保护的机器里，运行环境不能被信任。为了弥补这个缺陷，安全策略经常要求在应用程序执行之前对应用程序建立信任，例如要求程序员在执行从网上获得的应用程序前，先检查病毒和源代码中不安全的代码。这种方法有2个问题：(1)确定程序是否安全的检查很复杂很浪费时间，很少有程序员愿意花时间读程序的源代码，然后再将它编译成本地机器码以保证程序是安全的。(2)病毒检查要不断维护以保证有效性。
　　JAVA的安全模型是不同的，为了防止用户系统受到通过网络下载的不安全程序的破坏，JAVA提供了1个自定义的可以在里面运行JAVA程序的“沙盒”。JAVA的安全模型使得JAVA成为适合于网络环境的技术。JAVA的安全性允许用户从Internet或Intranet上引入或运行applet，applet的行动被限制于它的“沙盒”，applet可以在沙盒里做任何事情，但不能读或修改沙盒外的任何数据，沙盒可以禁止不安全程序的很多活动，如：
　　1.对硬盘进行读写。
　　2.和别的主机(不包括程序所在的主机)进行网络连接。
　　3.创建1个新过程。
　　4.载入1个新的动态库并直接调用本地方法。
　　“沙盒”模型的思想是在信任的环境中运行不信任的代码，这样即使用户不小心引入了不安全的applet，applet也不会对系统造成破坏。
1　“沙盒”模型
　　“沙盒”安全模型是内建于JAVA结构的，它主要由以下几部分构成：
　　1.内建于JAVA虚拟机和语言的安全特性。
　　2.类的载入结构。
　　3.类文件校验器。
　　4.安全管理器和JAVA API。
　　下面分别进行阐述。
1.1　内建于JAVA虚拟机和语言的安全特性
　　JAVA语言有一些机制使得JAVA程序很稳健，这些机制也是JAVA虚拟机(JVM)的特点，它们是：
　　1.安全的类型转换。在C、C++中，可以通过指针进行任意的类型转换，但常常带来不安全性，而在JAVA中，运行时系统对对象的处理要进行类型相容性检查，以防止不安全的转换。
　　2.没有指针。指针是C和C++中最灵活、也最容易产生错误的数据类型。由指针所进行的内存地址操作常会造成不可预知的错误，同时通过指针对某个内存地址进行显示类型转换后，可以访问1个C++中的私有成员，从而破坏安全性，造成系统的崩溃。而JAVA对指针进行完全的控制，程序员不能直接进行任何指针操作。
　　3.自动的垃圾收集。在C中，程序员通过库函数malloc()和free()来分配和释放内存，C++中则通过运算符new和delete来分配和释放内存。再次释放已释放的内存块或未被分配的内存块，会造成系统的崩溃；同样，忘记释放不再使用的内存块也会逐渐耗尽系统资源。而在JAVA中，所有的数据结构都是对象，通过运算符new为它们分配内存堆。通过new得到对象的处理权，而实际分配给对象的内存可能随程序运行而改变，JAVA对此自动进行管理并且进行垃圾收集，有效地防止了由于程序员的误操作而导致的错误，并且更好地利用了系统资源。
　　4.数组界限检查。若程序中有数组访问，JVM就会检查数组访问是否越界，从而防止了由数组越界引起的错误。
　　5.检查null引用。若使用的引用为null，JVM就会抛出1个例外。
1.2　类载入器结构
　　类载入器结构在沙盒模型中起了重要作用。在虚拟机中，类载入器负责引入、定义运行程序的类和接口的二进制数据。在虚拟机中可能有不止1个类载入器。
　　1个JAVA程序能使用2种类型的类载入器：原始的类载入器和类载入器对象。原始的类载入器是JVM实现的1部分，它通常从本地硬盘载入安全的类，包括JAVA API的类。运行时，JAVA应用程序安装类载入器对象，类载入器对象使用JAVA语言编写，编译成类文件后，载入到虚拟机，像其他的对象一样初始化。类载入器对象用于以自定义的方式载入类，例如通过网络下载类文件。JVM把通过原始的类载入器载入的任何类都视为安全的，无论这个类是否是JAVA API的1部分。而将通过类载入器对象载入的类视为可怀疑的，缺省情况下视为不安全的。
　　JAVA的结构可以在1个应用程序中创建多个命名空间，命名空间是由1个特定的类载入器载入的类名的集合。JVM为每1个类载入器维持1个命名空间，这个命名空间里有由那个类载入器载入的所有类的名字。由不同类载入器载入的类在不同的命名空间里，没有程序的直接允许，这些类不能彼此访问。编写程序时，可以将来源不同的类放在不同的命名空间里，用这种方式，就可以使用JAVA的类载入器结构控制来源不同的代码间的相互作用，就可以防止不安全代码访问、破坏安全代码。对于Applet，也是同样的道理，通过将来源不同的Applet类文件放在不同的命名空间，防止不安全代码破坏安全代码。
　　在“沙盒”结构中，类载入器结构是防止不安全代码的第一道围墙，它的作用主要有2方面：
　　1.防止不安全代码访问、破坏安全代码。
　　2.防止不安全代码冒充安全的类。
　　这2方面作用是通过下述方法实现的：
　　1.将代码分放在多个命名空间中，并在不同命名空间的代码之间设置“屏障”。类载入器结构是通过命名空间来防止不安全代码访问、破坏安全代码。命名空间在载入到不同命名空间中的类之间设置了1个“屏障”。在JVM中，同一命名空间中的类可以直接相互作用，不同命名空间中的类甚至不能检测到对方的存在，除非程序允许它们相互作用。
　　2.保护可信任类库(像JAVA API)的边界。如果类载入器载入1个类，这个类用它的名字来冒充是JAVA API的1部分(例如，类名为java.lang.virus)，类载入器就传递请求给原始类载入器，如果原始类载入器不能载入这个类，类载入器就抛出安全例外，并拒绝载入这个类。
1.3　类文件校验器
　　每一个JVM都有1个类文件校验器，用来保证载入的类文件具有正确的内部结构。如果类文件校验器发现类文件有错误，它就抛出1个例外。
　　类文件校验器能帮助检查出类使用起来是否安全。因为类文件是由二进制数据构成的，JVM不知道这个类文件是否是由黑客产生的，是否有可能破坏虚拟机的完整性，所以虚拟机对引入的字节码进行校验是很重要的。类文件校验器校验的过程可以分为2个阶段：阶段1发生在类刚载入以后，类文件校验器检查类文件的内部结构，包括校验所含的字节码的完整性；阶段2发生在字节码执行的时候，字节码校验器确定符号引用的类、域和方法是否存在。
1.3.1内部检查
　　在阶段1，类文件校验器检查类文件是否正确组成、内部是否一致、是否遵循JAVA编程语言的限制、含有的字节码是否能由JVM安全执行，如果类文件校验器检查出错误，它就会抛出1个错误，类文件就不再被程序使用。
　　1.检查格式和内部一致性
　　在阶段1，校验器除了检查字节码的完整性，还要检查类文件的格式和内部一致性。例如，每一个类文件都必须以同样的4字节数字0xCAFEBASE开头，这样类文件校验器可能检查的第一件事就是引入的文件是否以0xCAFEBASE开头。
　　类文件校验器还要校验类文件的长度是否与类文件中所记录的长度一致，也即校验类文件是否完整，是否被截去一部分或者被添加。类文件校验器还要检查类是否遵循Java语言的规定，如除了Object类，所有的类都必须有1个父类等。
　　2.字节码校验
　　一旦类文件校验器成功地完成格式和内部一致性的检查，就开始对字节码进行校验。通过对代表类方法的字节码流进行数据流分析，进行操作码是否有效及操作码是否有有效的操作数等的检查，以验证字节码流是否可以由虚拟机安全执行。
1.3.2符号引用的校验
　　符号引用是给出了引用项的名字和其它信息的字符串，这些信息足以用来区分类、域或方法。符号引用的校验用来验证引用是否有效，如果引用无效(例如，如果类不能载入，或者类存在但不含有要引用的域或方法)，类文件校验器就会抛出1个错误。
1.4　安全管理器和JAVA API
　　安全管理器定义了“沙盒”的外部边界。安全管理器是类java.lang.SecurityManager的子类，它是自定义的。
　　JAVA API类在采取一些行动时，通常需要安全管理器检查这个行动是否安全，这些行动包括：
　　1.接受来自于特定主机的socket连接。
　　2.修改线程(改变线程优先级，结束线程等)。
　　3.开放对于特定主机的socket连接。
　　4.创建1个新的类载入器。
　　5.删除特定的文件。
　　6.创建新的过程。
　　7.程序退出。
　　8.调用含有本地方法的动态库。
　　9.等待连接。
　　10.从特定的包载入类。
　　11.给特定的包中添加1个新类。
　　12.访问或修改系统特性。
　　13.访问特定的系统特性。
　　14.读文件。
　　15.写文件。
　　由于在执行上述动作前需要安全管理器进行检查，JAVA API不执行安全管理器建立的安全措施所禁止的任何动作。
　　JAVA API执行1个可能不安全活动的过程，通常分为2步：首先，JAVA API代码检查安全管理器是否已安装。如果没有安装，就无法进行第2步的检查，则直接执行可能不安全的代码；如果安装了，就继续执行第2步的检查，调用安全管理器中合适的检查方法对动作进行检查，如果动作被禁止，检查方法就抛出1个安全例外，不再执行不安全的活动；如果动作被允许，检查方法返回，JAVA API方法继续执行动作。
2　结束语
　　安全的可靠性是和代价成正比的，安全的可靠性越高，为安全付出的代价也越高，因此安全的可靠性也并不是越高越好，应该与被保护的信息的价值相当。计算机或网络的安全策略应该是多方面的，仅仅有1个运行下载的JAVA代码的“沙盒”是不够的，还应包括人员、机器等本身的安全，例如机器是否设置有口令、雇员是否是商业间谍等。但是JAVA安全模型的好处在于一旦建立，就可以为程序员做很多事情，程序员不必担心程序是否安全，JAVA的安全结构已经对程序是否安全进行了确定，如果代码不安全，利用“沙盒”结构就可以保护信息免受破坏。
作者单位：北京中国科学院软件研究所(100080)
参考文献
　1　Jaworkski J.JavaDeveloper's Guide.Simon&Schuster publishing house,1996
　2　SunMicrosystems.Java Security，Sun's Security white-paper.1997
　3　Li G.Java Security:Present and Near Future,IEEE Micro,1997;17(3):15
　4　王克宏.Java虚拟机规范.北京：清华大学出版社，1996
　5　邱仲潘译.Java从入门到精通.北京：电子工业出版社，1997
(收稿日期：1998-06-29)
