本节书摘来异步社区《Java编码指南:编写安全可靠程序的75条建议》一书中的第1章,第1.19节,作者:【美】Fred Long(弗雷德•朗), Dhruv Mohindra(德鲁•莫欣达), Robert C.Seacord(罗伯特 C.西科德), Dean F.Sutherland(迪恩 F.萨瑟兰), David Svoboda(大卫•斯沃博达),更多章节内容可以访问云栖社区“异步社区”公众号查看。
默认的SecurityManager会检查给定方法的调用者是否具有足够的继续执行动作的权限。动作定义在Java安全架构的访问级别,需要特定的权限才能执行。例如,java.io.FilePermission类的动作是读、写、执行和删除[API 2013]。“权限描述和风险”指南(Permission Descriptions and Risks guide)[Oracle 2011d]列举了默认的权限和为Java代码授予这些权限有关的风险。
有时候,我们需要的限制比默认安全管理器所能提供的还要强。当不存在对应的默认权限且未能提供自定义的权限时,可能会导致特权升级漏洞,从而允许不可信的调用者执行限制操作或动作。
本指南讨论了过多权限的问题,有关解决这个问题的另外一个办法,参见指南16。
下面的违规代码示例包含一个特权代码块,用来执行两个敏感操作:加载一个库;设置默认异常处理程序。
class LoadLibrary {
private void loadLibrary() {
AccessController.doPrivileged(
new PrivilegedAction() {
public Object run() {
// Privileged code
System.loadLibrary("myLib.so");
// Perform some sensitive operation like
// setting the default exception handler
MyExceptionReporter.setExceptionReporter(reporter);
return null;
}
});
}
}```
使用时,默认的安全管理器会禁止库的加载,除非RuntimePermission loadLibrary.myLib在策略文件中已被授权。然而,安全管理器不会自动防护调用者的第二个敏感操作的执行,即设置默认异常处理程序,因为该操作的权限不是默认的,因此,安全管理器此时不会生效。这个安全弱点可以被利用,例如,编程并安装一个能泄露信息的异常处理程序,泄露那些合法处理程序会过滤掉的信息。
####合规解决方案
下面的合规解决方案定义了一个自定义的权限ExceptionReporterPermission,与目标exc.reporter,用以禁止非法调用者设置默认异常处理程序。这可以通过子类化BasicPermission来实现,它允许二进制风格的权限(允许或不允许)。该解决方案然后使用安全管理器,检查调用者是否拥有必要的设置异常处理程序的权限。如果检查失败,代码会抛出SecurityException异常。自定义权限类ExceptionReporterPermission还定义了所需的两个构造函数。
class LoadLibrary {
private void loadLibrary() {
AccessController.doPrivileged(
new PrivilegedAction() {
public Object run() {
// Privileged code
System.loadLibrary("myLib.so");
// Perform some sensitive operation like
// setting the default exception handler
MyExceptionReporter.setExceptionReporter(reporter);
return null;
}
});
}
}
final class MyExceptionReporter extends ExceptionReporter {
public void setExceptionReporter(ExceptionReporter reporter) {
SecurityManager sm = System.getSecurityManager();
if(sm != null) {
sm.checkPermission(
new ExceptionReporterPermission("exc.reporter"));
}
// Proceed to set the exception reporter
}
// ... Other methods of MyExceptionReporter
}
final class ExceptionReporterPermission extends BasicPermission {
public ExceptionReporterPermission(String permName) {
super(permName);
}
// Even though the actions parameter is ignored,
// this constructor has to be defined
public ExceptionReporterPermission(String permName,
String actions) {
super(permName, actions);
}
}`
策略文件需要授予两个权限:将ExceptionReporterPermission权限授予exc.reporter;将RuntimePermission权限授予loadlibrary.myLib。以下策略文件假设上述资源位于Windows系统的c:package目录下。
grant codeBase "file:/c:/package" {
//For *nix, file:${user.home}/package/
permission ExceptionReporterPermission "exc.reporter";
permission java.lang.RuntimePermission "loadLibrary.myLib";
};```
默认情况下,不能使用BasicPermission将权限定义为支持动作,如果需要的话,可以在ExceptionReporterPermission的子类中自由地实现这些动作。BasicPermission是一个抽象类,尽管它不包含抽象方法;它声明了所有从Permission类继承的方法。BasicPermission类的自定义子类必须定义两个构造函数,调用最合适的(单参数或双参数)超类构造函数(因为超类没有默认构造函数)。双参数构造函数也接受一个动作,即使基本权限不会使用它。从策略文件中构造权限对象时,需要这种行为。注意,BasicPermission类的自定义子类要被声明成final类。
####适用性