Java安全——理解Java沙箱

简介: Java安全——理解Java沙箱 标签: Java 安全 [toc] 什么是沙箱^1 Java安全模型的核心就是Java沙箱(sandbox),什么是沙箱?沙箱是一个限制程序运行的环境。限制程序运行一方面是为了保护系统资源,同时另一方面也为了保护程序自己。沙箱主要限制系统资源访问,那系统

Java安全——理解Java沙箱

标签: Java 安全


[toc]


什么是沙箱1

Java安全模型的核心就是Java沙箱(sandbox),什么是沙箱?沙箱是一个限制程序运行的环境。限制程序运行一方面是为了保护系统资源,同时另一方面也为了保护程序自己。沙箱主要限制系统资源访问,那系统资源包括什么?——CPU、内存、文件系统、网络。不同级别的沙箱对这些资源访问的限制也可以不一样。

所有的Java程序运行都可以指定沙箱,可以定制安全策略。而一个Java程序运行的安全策略,包括了以下几点基础:

  • 字节码校验器(bytecode verifier):确保Java类文件遵循Java语言规范。这样可以帮助Java程序实现内存保护。但是不是所有的类文件都会经过字节码校验,比如核心类。
  • 类加载器(class loader):所有的Java类都是通过类加载器加载的,可以自定义类加载器来设置加载类的权限。
  • 存取控制器(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 "<< 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端口等等。

另一个很重要的是参数文件——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:githubCDLibsrcmainresourcessecurityr.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.<init>(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文件修改了默认沙箱的安全策略,再通过启动参数开启沙箱模式。这样就可以构造我们自己想要的沙箱效果了。

2. 限制访问网络

通过HttpClient访问www.baidu.com

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;

import com.taobao.cd.http.util.HttpUtil;

public class PolicyTest {

    public static void network() {
        try {
            String text = HttpUtil.createHtmlText("http://www.baidu.com", HttpUtil.UA);
            System.out.println(text);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        // test use network.
        network();
    }
}

开启默认沙箱后,输出如下:

java.security.AccessControlException: access denied ("java.net.SocketPermission" "www.baidu.com" "resolve")

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.checkConnect(Unknown Source)
at java.net.InetAddress.getAllByName0(Unknown Source)
at java.net.InetAddress.getAllByName(Unknown Source)
at java.net.InetAddress.getAllByName(Unknown Source)
at org.apache.http.impl.conn.DefaultClientConnectionOperator.resolveHostname(DefaultClientConnectionOperator.java:242)
at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:130)
at org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:149)
at org.apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:121)
at org.apache.http.impl.client.DefaultRequestDirector.tryConnect(DefaultRequestDirector.java:573)
at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:425)
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:820)
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:754)
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:732)
at com.taobao.cd.http.util.HttpUtil.createHtmlText(HttpUtil.java:38)
at com.taobao.cd.security.PolicyTest.network(PolicyTest.java:15)
at com.taobao.cd.security.PolicyTest.main(PolicyTest.java:45)

根据错误提示,知道是访问socket没有权限。那么修改下policy,指定权限:

grant {
    permission java.net.SocketPermission "www.baidu.com:1-", "connect,resolve";
};

在指定权限文件下再运行,得到了正常的text形式的baidu首页的页面文档。权限策略成功。

代码参考github:https://github.com/changedi/CDLib/blob/master/src/main/java/com/taobao/cd/security/PolicyTest.java

附录

A

权限名 用途说明
accessClassInPackage.<name> 允许代码访问指定包中的类
accessDeclaredMembers 允许代码使用反射访问其他类中私有或保护的成员
createClassLoader 允许代码实例化类加载器
createSecurityManager 允许代码实例化安全管理器,它将允许程序化的实现对沙箱的控制
defineClassInPackage.<name> 允许代码在指定包中定义类
exitVM 允许代码关闭整个虚拟机
getClassLoader 允许代码访问类加载器以获得某个特定的类
getProtectionDomain 允许代码访问保护域对象以获得某个特定类
loadlibrary.<name> 允许代码装载指定类库
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.<provider name> 针对指定的提供者,删除所有属性
createAccessControlContext 允许创建一个存取控制器的上下文环境
getDomainCombiner 允许撤销保护域
getPolicy 检索可以实现沙箱策略的类
getProperty.<prop name> 读取指定的安全属性
getSignerPrivateKey 由Signer对象获取私有密钥
insertProvider.<provider name> 将指定的提供者添加到响应的安全提供者组中
loadProviderProperties.<provider name> 装载指定的提供者的属性
printIdentity 打印Identity类内容
putAllProviderProperties.<provider name> 更新指定的提供者的属性
putProviderProperty.<provider name> 为指定的提供者增加一个属性
removeIdentityCertificate 取消Identity对象的证书
removeProvider.<provider name> 将指定的提供者从相应的安全提供者组中删除
removeProviderProperty.<provider name> 删除指定的安全提供者的某个属性
setIdentityInfo 为某个Identity对象设置信息串
setIdentityPublicKey 为某个Identity对象设置公钥
setPolicy 设置可以实现沙箱策略的类
setProperty.<prop name> 设置指定的安全属性
setSignerKeyPair 在Signer对象中设置密钥对
setSystemScope 设置系统所用的IdentityScope

E

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

参考文献


  1. Java安全第二版
目录
相关文章
|
12天前
|
SQL 安全 Java
安全问题已经成为软件开发中不可忽视的重要议题。对于使用Java语言开发的应用程序来说,安全性更是至关重要
在当今网络环境下,Java应用的安全性至关重要。本文深入探讨了Java安全编程的最佳实践,包括代码审查、输入验证、输出编码、访问控制和加密技术等,帮助开发者构建安全可靠的应用。通过掌握相关技术和工具,开发者可以有效防范安全威胁,确保应用的安全性。
25 4
|
1月前
|
安全 Java 编译器
Java 泛型深入解析:类型安全与灵活性的平衡
Java 泛型通过参数化类型实现了代码重用和类型安全,提升了代码的可读性和灵活性。本文深入探讨了泛型的基本原理、常见用法及局限性,包括泛型类、方法和接口的使用,以及上界和下界通配符等高级特性。通过理解和运用这些技巧,开发者可以编写更健壮和通用的代码。
|
2月前
|
安全 Java API
java安全特性
java安全特性
29 8
|
2月前
|
安全 Java API
【性能与安全的双重飞跃】JDK 22外部函数与内存API:JNI的继任者,引领Java新潮流!
【9月更文挑战第7天】JDK 22外部函数与内存API的发布,标志着Java在性能与安全性方面实现了双重飞跃。作为JNI的继任者,这一新特性不仅简化了Java与本地代码的交互过程,还提升了程序的性能和安全性。我们有理由相信,在外部函数与内存API的引领下,Java将开启一个全新的编程时代,为开发者们带来更加高效、更加安全的编程体验。让我们共同期待Java在未来的辉煌成就!
65 11
|
2月前
|
安全 Java API
【本地与Java无缝对接】JDK 22外部函数和内存API:JNI终结者,性能与安全双提升!
【9月更文挑战第6天】JDK 22的外部函数和内存API无疑是Java编程语言发展史上的一个重要里程碑。它不仅解决了JNI的诸多局限和挑战,还为Java与本地代码的互操作提供了更加高效、安全和简洁的解决方案。随着FFM API的逐渐成熟和完善,我们有理由相信,Java将在更多领域展现出其强大的生命力和竞争力。让我们共同期待Java编程新纪元的到来!
98 11
|
3月前
|
安全 算法 Java
java系列之~~网络通信安全 非对称加密算法的介绍说明
这篇文章介绍了非对称加密算法,包括其定义、加密解密过程、数字签名功能,以及与对称加密算法的比较,并解释了非对称加密在网络安全中的应用,特别是在公钥基础设施和信任网络中的重要性。
|
3月前
|
Java
【Java集合类面试十二】、HashMap为什么线程不安全?
HashMap在并发环境下执行put操作可能导致循环链表的形成,进而引起死循环,因而它是线程不安全的。
|
3月前
|
安全 算法 Java
【Java集合类面试二】、 Java中的容器,线程安全和线程不安全的分别有哪些?
这篇文章讨论了Java集合类的线程安全性,列举了线程不安全的集合类(如HashSet、ArrayList、HashMap)和线程安全的集合类(如Vector、Hashtable),同时介绍了Java 5之后提供的java.util.concurrent包中的高效并发集合类,如ConcurrentHashMap和CopyOnWriteArrayList。
【Java集合类面试二】、 Java中的容器,线程安全和线程不安全的分别有哪些?
|
3月前
|
安全 Java 测试技术
深入探讨Java安全编程的最佳实践,帮助开发者保障应用的安全性
在网络安全日益重要的今天,确保Java应用的安全性成为了开发者必须面对的课题。本文介绍Java安全编程的最佳实践,包括利用FindBugs等工具进行代码审查、严格验证用户输入以防攻击、运用输出编码避免XSS等漏洞、实施访问控制确保授权访问、采用加密技术保护敏感数据等。此外,还强调了使用最新Java版本、遵循最小权限原则及定期安全测试的重要性。通过这些实践,开发者能有效提升Java应用的安全防护水平。
49 2
|
3月前
|
前端开发 开发者 安全
JSF支付功能大揭秘:探索如何在Java世界中实现安全无缝的在线支付体验
【8月更文挑战第31天】在电子商务和在线交易日益普及的今天,实现在线支付功能已成为许多Web应用的必备需求。JavaServer Faces (JSF) 作为一种流行的Java Web框架,提供了丰富的组件和工具来构建用户界面,包括与支付网关集成以实现在线支付。支付网关是处理信用卡和借记卡支付的系统,在商家和银行之间起到桥梁作用。本文将探讨如何使用JSF与支付网关集成,以及实现在线支付功能时需要考虑的关键点
47 0