乔微博 发表于 2022-3-26 10:35:24

java中的安全模型(沙箱机制)

参考:

[*]https://www.cnblogs.com/MyStringIsNotNull/p/8268351.html
[*]https://www.ibm.com/developerworks/cn/java/j-lo-javasecurity/
[*]https://docs.oracle.com/javase/6/docs/technotes/guides/security/spec/security-spec.doc.html
[*]https://docs.oracle.com/javase/6/docs/technotes/guides/security/permissions.html
[*]https://docs.oracle.com/javase/6/docs/technotes/guides/security/PolicyFiles.html
[*]https://www.ibm.com/developerworks/cn/java/j-jws19/
[*]https://www.ibm.com/developerworks/cn/java/j-lo-springsecurity/
[*]https://www.ibm.com/developerworks/cn/java/j-lo-j2eebank/
[*]https://www.ibm.com/developerworks/cn/java/
相关介绍:

  我们都知道,程序员编写一个Java程序,默认的情况下可以访问该机器的任意资源,比如读取,删除一些文件或者网络操作等。当你把程序部署到正式的服务器上,系统管理员要为服务器的安全承担责任,那么他可能不敢确定你的程序会不会访问不该访问的资源,为了消除潜在的安全隐患,他可能有两种办法:

[*]让你的程序在一个限定权限的帐号下运行。
[*]利用Java的沙箱机制来限定你的程序不能为非作歹。以下用于介绍该机制。
什么是沙箱?

  Java安全模型的核心就是Java沙箱(sandbox),什么是沙箱?沙箱是一个限制程序运行的环境。沙箱机制就是将 Java 代码限定在虚拟机(JVM)特定的运行范围中,并且严格限制代码对本地系统资源访问,通过这样的措施来保证对代码的有效隔离,防止对本地系统造成破坏。沙箱主要限制系统资源访问,那系统资源包括什么?——CPU、内存、文件系统、网络。不同级别的沙箱对这些资源访问的限制也可以不一样。
  所有的Java程序运行都可以指定沙箱,可以定制安全策略。
java中的安全模型:

  在Java中将执行程序分成本地代码和远程代码两种,本地代码默认视为可信任的,而远程代码则被看作是不受信的。对于授信的本地代码,可以访问一切本地资源。而对于非授信的远程代码在早期的Java实现中,安全依赖于沙箱 (Sandbox) 机制。如下图所示 JDK1.0安全模型

  但如此严格的安全机制也给程序的功能扩展带来障碍,比如当用户希望远程代码访问本地系统的文件时候,就无法实现。因此在后续的 Java1.1 版本中,针对安全机制做了改进,增加了安全策略,允许用户指定代码对本地资源的访问权限。如下图所示 JDK1.1安全模型

  在 Java1.2 版本中,再次改进了安全机制,增加了代码签名。不论本地代码或是远程代码,都会按照用户的安全策略设定,由类加载器加载到虚拟机中权限不同的运行空间,来实现差异化的代码执行权限控制。如下图所示 JDK1.2安全模型

  当前最新的安全机制实现,则引入了域 (Domain) 的概念。虚拟机会把所有代码加载到不同的系统域和应用域,系统域部分专门负责与关键资源进行交互,而各个应用域部分则通过系统域的部分代理来对各种需要的资源进行访问。虚拟机中不同的受保护域 (Protected Domain),对应不一样的权限 (Permission)。存在于不同域中的类文件就具有了当前域的全部权限,如下图所示 最新的安全模型(jdk 1.6)

  以上提到的都是基本的Java 安全模型概念,在应用开发中还有一些关于安全的复杂用法,其中最常用到的 API 就是 doPrivileged。doPrivileged 方法能够使一段受信任代码获得更大的权限,甚至比调用它的应用程序还要多,可做到临时访问更多的资源。有时候这是非常必要的,可以应付一些特殊的应用场景。例如,应用程序可能无法直接访问某些系统资源,但这样的应用程序必须得到这些资源才能够完成功能。
组成沙箱的基本组件:



[*]字节码校验器(bytecode verifier):确保Java类文件遵循Java语言规范。这样可以帮助Java程序实现内存保护。但并不是所有的类文件都会经过字节码校验,比如核心类。
[*]类装载器(class loader):其中类装载器在3个方面对Java沙箱起作用

[*]它防止恶意代码去干涉善意的代码;
[*]它守护了被信任的类库边界;
[*]它将代码归入保护域,确定了代码可以进行哪些操作。

  虚拟机为不同的类加载器载入的类提供不同的命名空间,命名空间由一系列唯一的名称组成,每一个被装载的类将有一个名字,这个命名空间是由Java虚拟机为每一个类装载器维护的,它们互相之间甚至不可见。
  类装载器采用的机制是双亲委派模式。

[*]从最内层JVM自带类加载器开始加载,外层恶意同名类得不到加载从而无法使用;
[*]由于严格通过包来区分了访问域,外层恶意的类通过内置代码也无法获得权限访问到内层类,破坏代码就自然无法生效。


[*]存取控制器(access controller):存取控制器可以控制核心API对操作系统的存取权限,而这个控制的策略设定,可以由用户指定。
[*]安全管理器(security manager):是核心API和操作系统之间的主要接口。实现权限控制,比存取控制器优先级高。
[*]安全软件包(security package):java.security下的类和扩展包下的类,允许用户为自己的应用增加新的安全特性,包括:

[*]安全提供者
[*]消息摘要
[*]数字签名
[*]加密
[*]鉴别

沙箱包含的要素:

1. 权限

  权限是指允许代码执行的操作。包含三部分:权限类型、权限名和允许的操作。权限类型是实现了权限的Java类名,是必需的。权限名一般就是对哪类资源进行操作的资源定位(比如一个文件名或者通配符、网络主机等),一般基于权限类型来设置,有的比如java.security.AllPermission不需要权限名。允许的操作也和权限类型对应,指定了对目标可以执行的操作行为,比如读、写等。如下面的例子:
permission java.security.AllPermission;    //权限类型permission java.lang.RuntimePermission "stopThread";    //权限类型+权限名permission java.io.FilePermission "/tmp/foo" "read";    //权限类型+权限名+允许的操作 标准权限:
说明类型权限名操作例子文件权限java.io.FilePermission文件名(平台依赖)读、写、删除、执行允许所有问价的读写删除执行:permission java.io.FilePermission “>”, “read,write,delete,execute”;。允许对用户主目录的读:permission java.io.FilePermission “${user.home}/-”, “read”;套接字权限java.net.SocketPermission主机名:端口接收、监听、连接、解析允许实现所有套接字操作:permission java.net.SocketPermission “:1-”, “accept,listen,connect,resolve”;。允许建立到特定网站的连接:permission java.net.SocketPermission “.abc.com:1-”, “connect,resolve”;属性权限java.util.PropertyPermission需要访问的jvm属性名读、写读标准Java属性:permission java.util.PropertyPermission “java.”, “read”;。在sdo包中创建属性:permission java.util.PropertyPermission “sdo.”, “read,write”;运行时权限java.lang.RuntimePermission多种权限名[见附录A]无允许代码初始化打印任务:permission java.lang.RuntimePermission “queuePrintJob”AWT权限java.awt.AWTPermission6种权限名[见附录B]无允许代码充分使用robot类:permission java.awt.AWTPermission “createRobot”; permission java.awt.AWTPermission “readDisplayPixels”;网络权限java.net.NetPermission3种权限名[见附录C]无允许安装流处理器:permission java.net.NetPermission “specifyStreamHandler”;。安全权限java.security.SecurityPermission多种权限名[见附录D]无序列化权限java.io.SerializablePermission2种权限名[见附录E]无反射权限java.lang.reflect.ReflectPermissionsuppressAccessChecks(允许利用反射检查任意类的私有变量)无完全权限java.security.AllPermission无(拥有执行任何操作的权限)无2. 代码源

   代码源是类所在的位置,表示为以URL地址。
3. 保护域

   保护域用来组合代码源和权限,这是沙箱的基本概念。保护域就在于声明了比如由代码A可以做权限B这样的事情。
4. 策略文件

  策略文件是控制沙箱的管理要素,一个策略文件包含一个或多个保护域的项。策略文件完成了代码权限的指定任务,策略文件包括全局和用户专属两种。
  为了管理沙箱,策略文件我认为是最重要的内容。JVM可以使用多个策略文件,不过一般两个最常用。一个是全局的:$JREHOME/lib/security/java.policy,作用于JVM的所有实例。另一个是用户自己的,可以存储到用户的主目录下。策略文件可以使用jdk自带的policytool工具编辑。
默认的策略文件我们先参考一下:
// Standard extensions get all permissions by defaultgrant codeBase "file:${{java.ext.dirs}}/*" {      permission java.security.AllPermission;};// default permissions granted to all domainsgrant {      // Allows any thread to stop itself using the java.lang.Thread.stop()      // method that takes no argument.      // Note that this permission is granted by default only to remain      // backwards compatible.      // It is strongly recommended that you either remove this permission      // from this policy file or further restrict it to code sources      // that you specify, because Thread.stop() is potentially unsafe.      // See the API specification of java.lang.Thread.stop() for more      // information.      permission java.lang.RuntimePermission "stopThread";      // allows anyone to listen on dynamic ports      permission java.net.SocketPermission "localhost:0", "listen";      // permission for standard RMI registry port      permission java.net.SocketPermission "localhost:1099", "listen";      // "standard" properies that can be read by anyone      permission java.util.PropertyPermission "java.version", "read";      permission java.util.PropertyPermission "java.vendor", "read";      permission java.util.PropertyPermission "java.vendor.url", "read";      permission java.util.PropertyPermission "java.class.version", "read";      permission java.util.PropertyPermission "os.name", "read";      permission java.util.PropertyPermission "os.version", "read";      permission java.util.PropertyPermission "os.arch", "read";      permission java.util.PropertyPermission "file.separator", "read";      permission java.util.PropertyPermission "path.separator", "read";      permission java.util.PropertyPermission "line.separator", "read";      permission java.util.PropertyPermission "java.specification.version", "read";      permission java.util.PropertyPermission "java.specification.vendor", "read";      permission java.util.PropertyPermission "java.specification.name", "read";      permission java.util.PropertyPermission "java.vm.specification.version", "read";      permission java.util.PropertyPermission "java.vm.specification.vendor", "read";      permission java.util.PropertyPermission "java.vm.specification.name", "read";      permission java.util.PropertyPermission "java.vm.version", "read";      permission java.util.PropertyPermission "java.vm.vendor", "read";      permission java.util.PropertyPermission "java.vm.name", "read";};   策略文件的内容格式就是这样,grant授权允许操作某个权限。这个默认的策略文件就指明了jdk扩展包可以有全部权限,允许代码stop线程,允许监听1099端口(1099号端口,是默认的服务器端RMI监听端口)等等。
  另一个很重要的是参数文件——java.security,这个文件和策略文件在同一个目录下。这个参数文件定义了沙箱的一些参数。比如默认的沙箱文件是这样的(截取部分):
# The default is to have a single system-wide policy file,# and a policy file in the user's home directory.policy.url.1=file:${java.home}/lib/security/java.policypolicy.url.2=file:${user.home}/.java.policy# whether or not we expand properties in the policy file# if this is set to false, properties (${...}) will not be expanded in policy# files.policy.expandProperties=true# whether or not we allow an extra policy to be passed on the command line# with -Djava.security.policy=somefile. Comment out this line to disable# this feature.policy.allowSystemProperty=true    policy.url.*这个属性指明了使用的策略文件,如上文所述,默认的两个位置就在这里配置,用户可以自行更改顺序和存储位置。而policy.allowSystemProperty指明是否允许用户自行通过命令行指定policy文件。
5. 密钥库

   保存密钥证书的地方。
默认沙箱

  通过Java命令行启动的Java应用程序,默认不启用沙箱。要想启用沙箱,启动命令需要做如下形式的变更:
    java -Djava.security.manager
  沙箱启动后,安全管理器会使用两个默认的策略文件来确定沙箱启动参数。当然也可以通过命令指定:
    java -Djava.security.policy=
  如果要求启动时只遵循一个策略文件,那么启动参数要加个等号,如下:
    java -Djava.security.policy==
如何使用

1. 限制读文件

这个例子很简单,首先写一个r.txt文件,里面的内容是“abcd”,再写个程序如下读取这个r.txt。
import java.io.File;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.IOException;import java.io.InputStream;public class PolicyTest {    public static void file() {      File f = new File("D:\\github\\CDLib\\src\\main\\resources\\security\\r.txt");      InputStream is;      try {            is = new FileInputStream(f);            byte[] content = new byte;            while (is.read(content) != -1) {                System.out.println(new String(content));            }      } catch (FileNotFoundException e) {            // TODO Auto-generated catch block            e.printStackTrace();      } catch (IOException e) {            // TODO Auto-generated catch block            e.printStackTrace();      }    }    public static void main(String[] args) {      // test read file.      file();    }} 发现输出是abcd。
接下来修改java启动参数,加入-Djava.security.manager,启动了安全沙箱。再运行,输出变成了异常
Exception in thread "main" java.security.AccessControlException: access denied ("java.io.FilePermission" "D:\github\CDLib\src\main\resources\security\r.txt" "read") at java.security.AccessControlContext.checkPermission(Unknown Source) at java.security.AccessController.checkPermission(Unknown Source) at java.lang.SecurityManager.checkPermission(Unknown Source) at java.lang.SecurityManager.checkRead(Unknown Source) at java.io.FileInputStream.(Unknown Source) at com.taobao.cd.security.PolicyTest.main(PolicyTest.java:15)   这里已经提示了,访问被拒绝,说明了沙箱启动,同时也验证了默认沙箱——禁止本地文件访问。
再来,我们构建一个custom.policy文件如下:
grant {    permission java.io.FilePermission "D:\\github\\CDLib\\src\\main\\resources\\security\\*", "read";}; 这里构建了一条安全策略——允许读取security目录下的文件。
修改启动命令,添加
-Djava.security.policy=D:\\github\\CDLib\\src\\main\\resources\\security\\custom.policy 再执行,结果输出了abcd。
  如上例。我们通过自定义policy文件修改了默认沙箱的安全策略,再通过启动参数开启沙箱模式。这样就可以构造我们自己想要的沙箱效果了。
附录

A
权限名用途说明accessClassInPackage.允许代码访问指定包中的类accessDeclaredMembers允许代码使用反射访问其他类中私有或保护的成员createClassLoader允许代码实例化类加载器createSecurityManager允许代码实例化安全管理器,它将允许程序化的实现对沙箱的控制defineClassInPackage.允许代码在指定包中定义类exitVM允许代码关闭整个虚拟机getClassLoader允许代码访问类加载器以获得某个特定的类getProtectionDomain允许代码访问保护域对象以获得某个特定类loadlibrary.允许代码装载指定类库modifyThread允许代码调整指定的线程参数modifyThreadGroup允许代码调整指定的线程组参数queuePrintJob允许代码初始化一个打印任务readFileDescriptor允许代码读文件描述符(相应的文件是由其他保护域中的代码打开的)setContextClassLoader允许代码为某线程设置上下文类加载器setFactory允许代码创建套接字工厂setIO允许代码重定向System.in、System.out或System.err输入输出流setSecurityManager允许代码设置安全管理器stopThread允许代码调用线程类的stop()方法writeFileDescriptor允许代码写文件描述符B
权限名用途说明accessClipboard允许访问系统的全局剪贴板accessEventQueue允许直接访问事件队列createRobot允许代码创建AWT的Robot类listenToAllAWTEvents允许代码直接监听事件分发readDisplayPixels允许AWT Robot读显示屏上的像素showWindowWithoutWarningBanner允许创建无标题栏的窗口C
权限名用途说明specifyStreamHandler允许在URL类中安装新的流处理器setDefaultAuthenticator可以安装鉴别类requestPassworkAuthentication可以完成鉴别D
权限名用途说明addIdentityCertificate为Identity增加一个证书clearProviderProperties.针对指定的提供者,删除所有属性createAccessControlContext允许创建一个存取控制器的上下文环境getDomainCombiner允许撤销保护域getPolicy检索可以实现沙箱策略的类getProperty.读取指定的安全属性getSignerPrivateKey由Signer对象获取私有密钥insertProvider.将指定的提供者添加到响应的安全提供者组中loadProviderProperties.装载指定的提供者的属性printIdentity打印Identity类内容putAllProviderProperties.更新指定的提供者的属性putProviderProperty.为指定的提供者增加一个属性removeIdentityCertificate取消Identity对象的证书removeProvider.将指定的提供者从相应的安全提供者组中删除removeProviderProperty.删除指定的安全提供者的某个属性setIdentityInfo为某个Identity对象设置信息串setIdentityPublicKey为某个Identity对象设置公钥setPolicy设置可以实现沙箱策略的类setProperty.设置指定的安全属性setSignerKeyPair在Signer对象中设置密钥对setSystemScope设置系统所用的IdentityScopeE
权限名用途说明enableSubstitution允许实现ObjectInputStream类的enableResolveObject()方法和ObjectOutputStream类的enableReplaceObject()方法enableSubclassImplementation允许ObjectInputStream和ObjectOutputStream创建子类,子类可以覆盖readObject()和writeObject()方法相关主题


[*]参考 Security Architecture,查看 Java 最新安全架构的文档。
[*]查看 Policy Permissions,了解更多权限设置相关内容。
[*]查看 Default Policy Implementation and Policy File Syntax,了解安全策略文件使用。
[*]查看 Java web 服务:web 服务安全性状态:在本文中,您将首先更多地了解 web 服务栈之间互操作性的安全问题。然后您将看到作者根据自己对本系列近期 10 余篇文章进行的研究,从正确性和可用性几个方面比较 Axis2、Metro 和 CXF 之间的不同。
[*]查看 使用 Spring Security 保护 Web 应用的安全:在Spring Security 为使用 Spring 框架的Web 应用提供了良好的支持。本文将详细介绍如何使用 Spring Security 框架为 Web应用提供安全支持,实现用户认证和授权两个方面的安全增强。
[*]查看 基于 J2EE网银系统的安全系统解决方案概述:本文介绍网络银行所普遍采用的安全技术和方案,将从数据和业务逻辑的两个角度详细地分析一般网络银行系统的安全需求,并据此引入以PPDRR 为安全模型的安全设计方案。
[*]developerWorks Java 技术专区:这里有数百篇关于 Java 编程各个方面的文章。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: java中的安全模型(沙箱机制)