本博文整合自:Java安全——理解Java沙箱Java 安全模型介绍Java的沙箱机制原理入门

相关介绍:

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

  1. 让你的程序在一个限定权限的帐号下运行。
  2. 利用Java的沙箱机制来限定你的程序不能为非作歹。以下用于介绍该机制。

什么是沙箱?

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

 所有的Java程序运行都可以指定沙箱,可以定制安全策略。

java中的安全模型:

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

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

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

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

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

组成沙箱的基本组件:

  • 字节码校验器(bytecode verifier):确保Java类文件遵循Java语言规范。这样可以帮助Java程序实现内存保护。但并不是所有的类文件都会经过字节码校验,比如核心类。

  • 类装载器(class loader):其中类装载器在3个方面对Java沙箱起作用

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

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

 类装载器采用的机制是双亲委派模式。1. 从最内层JVM自带类加载器开始加载,外层恶意同名类得不到加载从而无法使用;

2. 由于严格通过包来区分了访问域,外层恶意的类通过内置代码也无法获得权限访问到内层类,破坏代码就自然无法生效。

  • 存取控制器(access controller):存取控制器可以控制核心API对操作系统的存取权限,而这个控制的策略设定,可以由用户指定。

  • 安全管理器(security manager):是核心API和操作系统之间的主要接口。实现权限控制,比存取控制器优先级高。

  • 安全软件包(security package):java.security下的类和扩展包下的类,允许用户为自己的应用增加新的安全特性,包括:

    1. 安全提供者
    2. 消息摘要
    3. 数字签名
    4. 加密
    5. 鉴别

沙箱包含的要素:

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 "<< ALL FILES>>", "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.AWTPermission 6种权限名[见附录B] 允许代码充分使用robot类:permission java.awt.AWTPermission "createRobot"; permission java.awt.AWTPermission "readDisplayPixels";
网络权限 java.net.NetPermission 3种权限名[见附录C] 允许安装流处理器:permission java.net.NetPermission "specifyStreamHandler";。
安全权限 java.security.SecurityPermission 多种权限名[见附录D]
序列化权限 java.io.SerializablePermission 2种权限名[见附录E]
反射权限 java.lang.reflect.ReflectPermission suppressAccessChecks(允许利用反射检查任意类的私有变量)
完全权限 java.security.AllPermission 无(拥有执行任何操作的权限)

2. 代码源

 代码源是类所在的位置,表示为以URL地址。

3. 保护域

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

4. 策略文件

 策略文件是控制沙箱的管理要素,一个策略文件包含一个或多个保护域的项。策略文件完成了代码权限的指定任务,策略文件包括全局和用户专属两种。

 为了管理沙箱,策略文件我认为是最重要的内容。JVM可以使用多个策略文件,不过一般两个最常用。一个是全局的:$JREHOME/lib/security/java.policy,作用于JVM的所有实例。另一个是用户自己的,可以存储到用户的主目录下。策略文件可以使用jdk自带的policytool工具编辑。

默认的策略文件我们先参考一下:

// Standard extensions get all permissions by default
grant codeBase "file:${{java.ext.dirs}}/*" {
permission java.security.AllPermission;
};
// default permissions granted to all domains
grant {
// 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.policy
policy.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 <other args>

沙箱启动后,安全管理器会使用两个默认的策略文件来确定沙箱启动参数。当然也可以通过命令指定:

java -Djava.security.policy=<URL>

如果要求启动时只遵循一个策略文件,那么启动参数要加个等号,如下:

java -Djava.security.policy==<URL>

如何使用

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[1024];
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 设置系统所用的IdentityScope

E

权限名 用途说明
enableSubstitution 允许实现ObjectInputStream类的enableResolveObject()方法和ObjectOutputStream类的enableReplaceObject()方法
enableSubclassImplementation 允许ObjectInputStream和ObjectOutputStream创建子类,子类可以覆盖readObject()和writeObject()方法

回到目录|·(工)·)

K:java中的安全模型(沙箱机制)的更多相关文章

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

    java中的安全模型(沙箱机制) java安全沙箱(一)之ClassLoader双亲委派机制 java安全沙箱(二)之.class文件检验器 java安全沙箱(三)之内置于Java虚拟机(及语言)的安 ...

  2. java中的动态代理机制

    java中的动态代理机制 在java的动态代理机制中,有两个重要的类或接口,一个是 InvocationHandler(Interface).另一个则是 Proxy(Class),这一个类和接口是实现 ...

  3. Java中的内存处理机制和final、static、final static总结

    Java中的内存处理机制和final.static.final static总结   装载自:http://blog.csdn.net/wqthaha/article/details/20923579 ...

  4. 深度剖析java中JDK动态代理机制

    https://www.jb51.net/article/110342.htm 本篇文章主要介绍了深度剖析java中JDK动态代理机制 ,动态代理避免了开发人员编写各个繁锁的静态代理类,只需简单地指定 ...

  5. Java 中的等待唤醒机制透彻讲解

    线程的状态 首先了解一下什么是线程的状态,线程状态就是当线程被创建(new),并且启动(start)后,它不是一启动就进入了执行状态(run),也不是一直都处于执行状态. 这里说一下Java 的Thr ...

  6. 深入Java核心 Java中多态的实现机制(1)

    在疯狂java中,多态是这样解释的: 多态:相同类型的变量,调用同一个方法时,呈现出多中不同的行为特征, 这就是多态. 加上下面的解释:(多态四小类:强制的,重载的,参数的和包含的) 同时, 还用人这 ...

  7. 在JAVA中,关于反射机制的讨论

    一.什么是反射机制         简单的来说,反射机制指的是程序在运行时能够获取自身的信息.在java中,只要给定类的名字,     那么就可以通过反射机制来获得类的所有信息. 二.哪里用到反射机制 ...

  8. java中实现多态的机制是什么?

    多态性是面向对象程序设计代码重用的一个重要机制,我们曾不只一次的提到Java多态性.在Java运行时多态性:继承和接口的实现一文中,我们曾详细介绍了Java实现运行时多态性的动态方法调度:今天我们再次 ...

  9. Java中的垃圾回收机制

    1. 垃圾回收的意义 在C++中,对象所占的内存在程序结束运行之前一直被占用,在明确释放之前不能分配给其它对象:而在Java中,当没有对象引用指向原先分配给某个对象的内存时,该内存便成为垃圾.JVM的 ...

随机推荐

  1. linux主机名

    [linux主机名] 1.查看主机名 主机名即是一台主机的名字,在终端窗口中输入命令:hostname或uname –n,均可以查看到当前主机的主机名. 2.临时修改主机名 命令行下运行命令:“hos ...

  2. 聊一下Python的线程 & GIL

    再来聊一下Python的线程 参考这篇文章 https://www.zhihu.com/question/23474039/answer/24695447 简单地说就是作为可能是仅有的支持多线程的解释 ...

  3. FIX protocol tutorial : Fix Session is not connecting how to diagnose it ?

    In this blog post of FIX protocol tutorial series I would like to share my experience with connectiv ...

  4. 电商商品规则属性sku 工具类

    /** * 商品模块: * 使用:var SKU = sku.init({ id:'sku', item: item, data:data }); * @config item = [{"i ...

  5. HDU 6065 RXD, tree and sequence (LCA+DP)

    题意:给定上一棵树和一个排列,然后问你把这个排列分成m个连续的部分,每个部分的大小的是两两相邻的LCA的最小深度,问你最小是多少. 析:首先这个肯定是DP,然后每个部分其实就是里面最小的那个LCA的深 ...

  6. JQuery中button提交表单报TypeError: elem[type] is not a function jquery

    错误: TypeError: elem[type] is not a function jquery 解决: 出现这种现象的原因是,提交的表单中,有标签的name,有以submit命名的 name中不 ...

  7. Linux 基础教程 33-硬盘分区及挂载

    挂载命令     在Windows系统中如果插入了U盘.移动硬盘.光驱等,只要能被Windows系统识别出来,则系统会进行自动挂载并添加盘符,然后我们就可以访问,而这一切均由系统完成,用户并不需要做任 ...

  8. EBS R12 Vision Profile default value - IRC: Geocode Host

    Profile Option Name Site Application Responsibility Server Server Org User IRC: Geocode Host http:// ...

  9. SQL Server触发器的基本使用

    sqlserver_SQL触发器的使用及语法(转自:百度文库) 定义: 何为触发器?在SQL Server里面也就是对某一个表的一定的操作,触发某种条件,从而执行的一段程序.触发器是一个特殊的存储过程 ...

  10. 更改mysql默认字符集 (转载)

    1. service mysqld stop,停用mysql. 2. cp /etc/my.cnf /etc/my.cnf.bak,修改前做备份,这是个好习惯. 修改my.cnfvi /etc/my. ...