《Java编码指南:编写安全可靠程序的75条建议》—— 指南20:使用安全管理器创建一个安全的沙盒

简介: 安全管理器是一个类,它允许应用程序实现一个安全策略。在执行一个可能不安全或敏感的操作前,它允许应用程序确定这个操作是什么,它是否正在一个安全的允许执行这个操作的上下文中进行尝试。该应用程序可以允许或禁止该操作。

本节书摘来异步社区《Java编码指南:编写安全可靠程序的75条建议》一书中的第1章,第1.20节,作者:【美】Fred Long(弗雷德•朗), Dhruv Mohindra(德鲁•莫欣达), Robert C.Seacord(罗伯特 C.西科德), Dean F.Sutherland(迪恩 F.萨瑟兰), David Svoboda(大卫•斯沃博达),更多章节内容可以访问云栖社区“异步社区”公众号查看。

指南20:使用安全管理器创建一个安全的沙盒

根据Java API中SecurityManager类的文档[API 2013]:

安全管理器是一个类,它允许应用程序实现一个安全策略。在执行一个可能不安全或敏感的操作前,它允许应用程序确定这个操作是什么,它是否正在一个安全的允许执行这个操作的上下文中进行尝试。该应用程序可以允许或禁止该操作。

安全管理器可以与任何Java代码相关联。

applet的安全管理器拒绝applet最基本特权以外的所有其他特权。如此设计旨在防止无意的系统修改、信息泄漏和用户冒名。安全管理器的使用并不局限于客户端保护。比如Tomcat和WebSphere这样的Web服务器,使用安全管理器来隔离木马servlet和恶意的Java服务器页面(JSP),保护敏感的系统资源不被无意访问。

从命令行运行的Java应用程序,可以通过命令行参数标志来设置默认的安全管理器或者自定义的安全管理器。另外,可以通过编程方式安装一个安全管理器。以编程方式安装安全管理器能促使程序创建一个默认的沙盒,这个沙盒会基于当前生效的安全策略来允许或拒绝敏感动作。

从Java 2 SE平台开始,SecurityManager不再是一个抽象类。因此,不再需要显式覆盖它的方法。以编程方式创建和使用安全管理器时,代码必须具有运行时权限来调用createSecurityManager(实例化SecurityManager)和setSecurityManager(安装它)。这些权限只在安全管理器已经安装时才被检查。在有些情况下,这很有用,比如在一个虚拟主机上有一个默认的安全管理器,它必须拒绝个人主机以自定义的安全管理器来覆盖默认的安全管理器。

安全管理器与AccessController类密切相关,前者是访问控制的中心,后者提供了访问控制算法的实际实现。安全管理器支持以下两项。

提供向后兼容性:老程序通常包含自定义安全性管理器类的实现,因为它最初是抽象类。
定义自定义策略:通过子类化安全管理器来允许定义自定义的安全策略(如多层次、粗粒度或细粒度)。
关于自定义安全管理器与默认安全管理器的实现和使用,Java安全架构规范(Java security architecture specification) [SecuritySpec 2010]中是这么声明的:

我们鼓励在应用程序代码中使用AccessController类,而定制安全管理器(通过子类化)应该是最后的手段,应当十分小心。此外,一个定制的安全管理器,如在调用标准安全检查前总是检查当前时间,在合适的时候可以而且应该使用AccessController所提供的算法。

许多Java SE API在执行敏感操作前,都会默认执行安全管理器检查。例如,java.io.FileInputStream的构造函数,如果调用者没有足够的读取文件的权限,它就会抛出SecurityException异常。因为SecurityException安全异常是RuntimeException运行时异常的一个子类,所以一些API方法(如java.io.FileReader类的方法)的声明可能缺乏列出SecurityException的throws语句。要避免依赖于没有在API方法文档中指定的安全管理器检查是否存在。

违规代码示例(命令行安装)

下面的违规代码示例没有从命令行安装任何安全管理器。因此,程序运行时启用了所有权限,也就是说,没有安全管理器来防止程序可能执行的任何邪恶动作。

java LocalJavaApp```
合规解决方案(默认策略文件)
任何Java程序都可以尝试以编程方式安装SecurityManager,尽管当前活动的安全管理器可能禁止此操作。被设计为本地运行的应用程序可以在调用时通过命令行参数指定一个默认的安全管理器。

当必须禁止应用程序以编程方式安装定制安全管理器,并且在任何情况下都要遵守默认的安全策略时,使用命令行选项更好。下面的合规解决方案使用了合适的命令行参数来安装默认安全管理器。安全策略文件为应用程序的预期动作授予了权限。

java -Djava.security.manager -Djava.security.policy=policyURL \
   LocalJavaApp`
命令行参数标志可以指定一个自定义的安全管理器,其策略被全局执行。使用-Djava.security.manager标志,如下所示:

java -Djava.security.manager=my.security.CustomManager ...```
如果由当前安全管理器执行的当前安全策略禁止替换(通过省略Runtime Permission("setSecurityManager")),那么任何试图调用setSecurity Manager()的方法都会抛出SecurityException异常。

在类Unix系统及其等效的微软Windows系统的/path/to/java.home/ lib/security目录中,可以找到默认的安全策略文件java.sercurity,它负责许多权限(读取系统属性、绑定到未授权端口等)的授予。一个特定于用户的策略文件也可能位于该用户的主目录中。这些策略文件的集合用来指定为程序授予的权限。java.security文件可以指定使用的策略文件。如果系统级的java.policy文件或java.security文件其中之一被删除,那么就没有用来执行Java程序的权限。

####合规解决方案(定制策略文件)
当以一个定制的策略文件覆盖全局Java安全策略时,要使用双等号(==)替换单等号(=):

java -Djava.security.manager \
   -Djava.security.policy==policyURL \
   LocalJavaApp`

合规解决方案(附加策略文件)

appletviewer自动安装了一个带有标准策略文件的安全管理器,并且使用-J标志指定了附加的策略文件。

appletviewer -J-Djava.security.manager \
       -J-Djava.security.policy==policyURL LocalJavaApp```
注意,当安全属性文件(java.security)中的policy.allowSystemProperty属性值被设置为false时,参数中指定的策略文件就会被忽略;该属性的默认值为true。默认策略实现和策略文件语法(Default Policy Implementation and Policy File Syntax)[Policy 2010]深入讨论了编写策略文件时的问题和语法。

####违规代码示例(编程式安装)
也可以使用静态的System.setSecurityManager()方法来激活SecurityManager。同时只能有一个SecurityManager处于激活状态。这个方法以参数中提供的SecurityManager或null来取代当前活动的SecurityManager。

下面的违规代码示例在让当前安全管理器失效的同时,并没有在原来位置上安装另一个安全管理器。因此,后续代码将在启用所有权限的状态下运行,对程序可能执行的任何邪恶动作都没有了限制。

try {
 System.setSecurityManager(null);
} catch (SecurityException se) {
 // Cannot set security manager, log to file
}`
一个实施合理安全策略的活动的SecurityManager能阻止系统令其失效,让这段代码抛出SecurityException异常。

合规解决方案(默认安全管理器)

下面的合规解决方案对默认安全管理器进行了实例化和设置。

try {
 System.setSecurityManager(new SecurityManager());
} catch (SecurityException se) {
 // Cannot set security manager, log appropriately
}```
####合规解决方案(自定义安全管理器)
下面的合规解决方案演示了如何实例化一个名为CustomSecurityManager的自定义安全管理器。程序首先通过密码调用了它的构造函数,然后将这个自定义安全管理器安装成了活动的安全管理器。

char password[] = / 初始化 /
try {
 System.setSecurityManager(
  new CustomSecurityManager("password here")
 );
} catch (SecurityException se) {
 // Cannot set security manager, log appropriately
}`
这段代码执行后,执行安全检查的API将使用这个自定义安全管理器。如前所述,只有当默认安全管理器缺少所需要的功能时,才考虑安装自定义安全管理器。

适用性

Java的安全性从根本上取决于安全管理器的存在。当其不存在时,敏感动作可以无限制地执行。

在运行时编程检测安全管理器的存在与否是很简单的。静态分析可以解决此类代码的存在与否,如果该代码被执行,它将试图安装一个安全管理器。在某些情况下,可以检查安全管理器是否安装得够早、是否指定了所需的属性,或者是否可以保证会被安装,但通常情况下是不可判定的。

当已知默认的全局安全管理器总是会从命令行安装时,对setSecurity Manager()方法的调用在受控环境中可能会被忽略。这很难实施,如果环境配置不正确,会出现漏洞。

相关文章
|
3月前
|
Java
Java开发实现图片URL地址检验,如何编码?
【10月更文挑战第14天】Java开发实现图片URL地址检验,如何编码?
100 4
|
3月前
|
Java 流计算
利用java8 的 CompletableFuture 优化 Flink 程序
本文探讨了Flink使用avatorscript脚本语言时遇到的性能瓶颈,并通过CompletableFuture优化代码,显著提升了Flink的QPS。文中详细介绍了avatorscript的使用方法,包括自定义函数、从Map中取值、使用Java工具类及AviatorScript函数等,帮助读者更好地理解和应用avatorscript。
利用java8 的 CompletableFuture 优化 Flink 程序
|
3月前
|
Java
Java实现随机生成某个省某个市的身份证号?如何编码?
【10月更文挑战第18天】Java实现随机生成某个省某个市的身份证号?如何编码?
188 5
|
3月前
|
Java
Java开发实现图片地址检验,如果无法找到资源则使用默认图片,如何编码?
【10月更文挑战第14天】Java开发实现图片地址检验,如果无法找到资源则使用默认图片,如何编码?
72 2
|
1天前
|
自然语言处理 Java
Java中的字符集编码入门-增补字符(转载)
本文探讨Java对Unicode的支持及其发展历程。文章详细解析了Unicode字符集的结构,包括基本多语言面(BMP)和增补字符的表示方法,以及UTF-16编码中surrogate pair的使用。同时介绍了代码点和代码单元的概念,并解释了UTF-8的编码规则及其兼容性。
73 60
|
4月前
|
XML 存储 JSON
Java程序部署
Java程序部署
|
2月前
|
IDE Java 编译器
开发 Java 程序一定要安装 JDK 吗
开发Java程序通常需要安装JDK(Java Development Kit),因为它包含了编译、运行和调试Java程序所需的各种工具和环境。不过,某些集成开发环境(IDE)可能内置了JDK,或可使用在线Java编辑器,无需单独安装。
83 1
|
2月前
|
SQL 安全 Java
安全问题已经成为软件开发中不可忽视的重要议题。对于使用Java语言开发的应用程序来说,安全性更是至关重要
在当今网络环境下,Java应用的安全性至关重要。本文深入探讨了Java安全编程的最佳实践,包括代码审查、输入验证、输出编码、访问控制和加密技术等,帮助开发者构建安全可靠的应用。通过掌握相关技术和工具,开发者可以有效防范安全威胁,确保应用的安全性。
57 4
|
3月前
|
存储 缓存 Java
java基础:IO流 理论与代码示例(详解、idea设置统一utf-8编码问题)
这篇文章详细介绍了Java中的IO流,包括字符与字节的概念、编码格式、File类的使用、IO流的分类和原理,以及通过代码示例展示了各种流的应用,如节点流、处理流、缓存流、转换流、对象流和随机访问文件流。同时,还探讨了IDEA中设置项目编码格式的方法,以及如何处理序列化和反序列化问题。
95 1
java基础:IO流 理论与代码示例(详解、idea设置统一utf-8编码问题)
|
2月前
|
SQL 安全 Java
Java 异常处理:筑牢程序稳定性的 “安全网”
本文深入探讨Java异常处理,涵盖异常的基础分类、处理机制及最佳实践。从`Error`与`Exception`的区分,到`try-catch-finally`和`throws`的运用,再到自定义异常的设计,全面解析如何有效管理程序中的异常情况,提升代码的健壮性和可维护性。通过实例代码,帮助开发者掌握异常处理技巧,确保程序稳定运行。
46 0