RMI中的安全策略

以下翻译来自Java RMI的Chapter 20.Security Policies

20.3 安全管理器

在一个运转的JVM中,权限是被SecurityManager的实例强制执行的。当一个程序试图做一些需要权限的事情时,就会通过查询SecurityManager的实例来检查此操作是否可以执行。举例来说,当试图从文件中读取数据时,将包含询问安全器程序是否允许从文件中读取数据的过程。

为了能看到实际效果,让我们看一下FileInputStream类的构造函数:



public FileInputStream(String name)throws FileNotFoundException{

SecurityManager security = System.getSecurityManager();

if(security!=null){
security.checkRead(name);
}

fd = new FileDescriptor();

open(name);
}



构造函数的第一件事情是获取SecurityManager的实例,并询问文件是否可读。如果程序没有读取文件的权限,将会抛出异常。相似地,Socket的构造函数也会调用已安装的安全管理器来检查它是否被允许连接:


private Socket(InetAddress address,int port,InetAddress localAddr,int localPort,boolean stream)throws IOException{
this();
if(port<0||port>0xFFFF){
throw new IllegalArgumentException("port out range:"+port);
}
if(localPort<0||localPort>0xFFFF){
throw new IllegalArgumentException("port out range:"+localPort);
}
SecurityManager security = System.getSecurityManager();
if(security!=null){
security.checkConnection(address.getHostAddress(),port);
}
....
}



但值得特别注意的是,如果没有安装SecurityManager的实例,那么权限检查将不会发生。这是一种一般性模式:程序只有在安装了管理器的情况下,才会执行权限检查,在没有安装的情况下,程序有权执行任何操作。

20.3.1 安装SecurityManager实例

在一个JVM中只能安装一个SecurityManager实例,并且此实例是不能被替换的。安装安全器最简单的方式是在运行JVM时设置java.security.manager系统属性,就像下面的示例一样:


java -Djava.security.manager application


上面的代码将会创建一个SecurityManager实例,在JVM启动之后,此实例将在特定应用程序开始运行之前安装。使用系统属性来安装安全器通常来说是非常便利的。举例来说,当我从Internet上下载了一个程序后,通常情况下我会从命令行安装一个安全管理器,其目的是限制恶意程序引发的损害。你也可以使用java.security.manager参数来指定一个特定的安全管理器,就像下面的代码一样:


java -Djava.security.manager=java.rmi.RMISecurityManager application


然而通常情况下,当你在编写一个RMI应用程序时,你可以通过调用System.setSecurityManager()来包含一个安全器。就像下面的代码一样:


System.setSecurityManager(new RMISecurityManager());


这样做的理由很简单:除非你的应用程序已经安装了SecurityManager实例,否则RMI的动态加载特性不会工作.如果你在使用动态加载时,依赖于用户或一个批处理文件,那么安装SecurityManager的实例是一个相当差的主意。

RMI提供了一个简单的安全管理器-RMISecurityManager(位于java.rmi.包下).在Java 2中,RMISecurityManager只是简单地继承标准安全器,其实现没有加入任何额外的功能。大部分RMI应用程序使用RMISecurityManager都基于两个非常简单的理由:

首先,RMISecurityManager而非标准安全管理器有效地记载了安装管理器的原因(因此,使得其他人在未经思考的情况下,通过修改代码来删除安全管理器的可能性就会变得很低)。

第二, 它会随着RMI的安全模型自动演变。如果你使用RMISecurityManager的实例,当其实现变得更精细的时候,你的代码也会自动演变。

20.3.2 安全管理器是如何工作的

正如我已经指明的,安全管理器是能通过电话交谈(call-in)策略来工作的。换句话说,每当可疑问题将要发生时,它都需要依赖于软件包来询问权限。安全顺序为:

1.请求安全检查的方法被调用

2.作为方法实现的一部分,被迫调用安全管理器。此调用通过抛出异常来表示发生了安全违反或通过默默地返回以允许原来的方法调用能够继续处理。

SecurityManager的实现为了检查是否允许一个操作,实际上做了许多的工作。它们首先检查调用SecurityManager的对象是否有适当的权限。注意,因为调用SecurityManager的对象是标准Java库中的某一部分,因此默认地它是有权限的。属标准Java库的所有类都有权限执行任何操作。但,它们必须检查他们是否能够执行请求的操作。在这之后,SecurityManager将遍历堆栈,检查堆栈里的每个类是否有合适的权限。如果其中任何一个没有权限的话,那么将检查失败并抛出异常。检查堆栈是安全机制中非常重要的一部分。除非整个堆栈都已经检查了,否则没有任何方式能直接地阻止敌对代码来初始化一个动作。

20.3.3 java.security.debug

在SecurityManager工作的时候,你可以通过设置java.security.debug系统属性来观察。此属性有四个基本值:all,access,policy,scl.

access是关于将要被打印到System.err的权限检查原因信息。access有三个高级版本:statck,domain和failure.access statck用于打印每次权限检查的信息。access domain打印堆栈中所有对象的域信息。access failure打印在堆栈或域中安全检查失败的信息。举例来说,在我们银行的程序中,通过下面的命令行来给出关于正在检查哪一个权限以及从哪检查的信息:

java -Djava.security.manager -Djava.security.debug = "access domain" com.ora.rmibook.chpter9.applications.BankClient.

20.4 设置安全策略

到目前为止,我们谈论的都是关于权限的基础思想,怎样检查它们以及强制执行。下一步我们讨论如何设置的。JVM是怎样查找出哪一个权限被授予了特定的类的呢?

20.4.1三个策略文件

在大部分情况下,当运行一个应用程序时,会用到下面的两个或三个安全策略文件:

1.全局策略文件

这是一个适合于由任何一个用户启动的所有应用程序的策略文件。它要么是默认策略文件(随Java运行时环境出现),要么是由系统管理员定义的策略文件。终端用户很少修改此文件。默认情况下,全局策略文件安装在${java.home}/jre/lib/security/java.policy.

2.用户策略文件

这是一个适合于由特定用户启动的所有应用程序的策略文件。它通常不存在(没有与JRE相关的特定用户的策略文件)或由系统管理员定义。终端用户很少修改此文件。用户策略文件有一个默认位置:${user.home}/.java.policy.

3.应用策略文件

这是一个适合于特定应用程序的策略文件,它定义了应用程序为了能够正常运行所需要的权限。此策略文件没有默认位置。相反地,其文件位置必须在程序运行的时候,以系统参数的形式进行设置。

因为每个文件都列出了权限,使得将这些文件进行联合就变得很有意义。也就是说,只要这些文件中任何一个指明一个操作是被允许的话,那么此操作就是允许的。

20.4.1.1 设置特定应用的策略文件

应用程序策略文件是通过java.security.policy系统参数来定义的。也就是说,为了设置它,你可以像下面这样使用命令行来设置:



java -Djava.security.policy="d:\java.policy"



然而在没有安全器的情况下,这也没有太多的意义。如果一个JVM是通过安全策略调用的,那么就相当于从命令行安装了一个安全管理器,就像下面这样:

java -Djava.security.manager -Djava.security.policy="d:\java.policy"


也可以写成:

java -Djava.security.manager -Djava.security.policy=="d:\java.policy"


上述配置将会告知JVM只使用应用策略文件而忽略其他两个权限文件。

20.4.2 策略文件内部

一个策略文件由grant语句序列组成的。每个grant语句都有下面的格式:

grant [signedBy Name] [codeBase URL] {
//list of persmissions
};

翻译过来就是:

任何一个从URL指定的codeBase加载的且使用Name数字签名的类,都要下列权限。

方括号表明是一个可选元素,单词"signedBy"和codeBase必须用在它们的参数前。因此,下面的全是正确的grant语句:


grant{
// list of permissions
};

grant codeBase "http://www.oreilly.com/"{
// list of permissions
}

grant signedBy WGrosso{
// list of permissions
};

grant signedBy WGrosso codeBase "http://oreilly.com/"{
// list of permissions
};



以下是一个较为完整的例子:



grant codeBase "file://d:/classes"{
permission java.awt.AWTPermission "accessClipboard";
permission java.awt.AWTPermission "accessEventQueue";
permission java.awt.AWTPermission "listenTOALLAWTEvents";
permission java.awt.AWTPermission "showWindowWithOutWarningBanner";
permission java.awt.AWTPermission "readDisplayPixels";

permission java.net.SocketPermission ":1024-","accept,connect,listen,resolve";

permission java.net.FilePermission "<<ALL FILES>>","read";
permission java.net.FilePermission "<<ALL FILES>>","write";
permission java.net.FilePermission "<<ALL FILES>>","delete";

permission java.util.PropertyPermission "*","read,write";

}


上面的配置允许从D:\classed目录加载的任何类操作GUI,使用非系统端口创建socket连接,操作文件,以及编辑系统属性对象。


20.4.3 在RMI中使用安全策略

在你的RMI应用程序中是没有必要使用安全管理器的,这只会影响到RMI的动态类加载特性.但是,RMI注册表和激活框架守护线程都要使用到安全策略.

如果你使用了这些服务中的任何一种,并且没有给它们一个专门的安全策略,那么你的应用程序将会以几种微妙的方式中断.考虑一个绑定一个使用定制socket的服务到RMI注册表中的例子.RMI注册表将包含一个指向原始服务器的stub实例.这个反序列化的stub将包含一个相关的socket.由于此相关的socket是定制socket类的实例,这说意味着java.net.socket构造函数的调用栈包含一个non-Javasoft的类.也就是说,除非RMI注册表拥有了一组更加不严格的权限,否则绑定会失败.

//

重申一下:-J标志是用来传递参数给底层JVM(包括RMI注册表和激活框架守护线程)的.比如,你必须使用下面的命令行来调用:
registry -J-Djava.security.policy="d:\classes"
//

因为动态类加载是非常有用的,又因为激活框架守护线程或注册表强制你无论如何都要编写一个安全管理器,因此RMI应用程序会使用一组相关的安全策略:服务端一个,命名服务一个,客户端一个.

20.4.4 策略工具
本章节因与JDK1.6中策略工具在界面上有一点出入,所以未翻译.想了解的朋友可以参考jdk1.6的安全指南.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值