《Java编码指南:编写安全可靠程序的75条建议》—— 指南16:避免授予过多特权

简介: Java安全策略为代码授予权限,用来允许指定代码访问特定的系统资源。一个被授予许可的代码源(CodeSource类型的对象),是由代码位置(URL)和证书引用组成的,该证书包含公钥以及与之对应的私钥,用来对代码进行数字签名, 代码只有在被某证书数字签名之后,才能关联到该证书引用。

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

指南16:避免授予过多特权

Java安全策略为代码授予权限,用来允许指定代码访问特定的系统资源。一个被授予许可的代码源(CodeSource类型的对象),是由代码位置(URL)和证书引用组成的,该证书包含公钥以及与之对应的私钥,用来对代码进行数字签名, 代码只有在被某证书数字签名之后,才能关联到该证书引用。代码只有在被某证书数字签名之后,才能关联到该证书引用。保护域(protection domain)包含一个CodeSource对象,以及CodeSource中的代码被授予的权限,这是由当前生效的安全策略所决定的。因此,用相同的密钥来进行签名的、来自相同URL的类,会被放置在相同的保护域中。一个类仅仅属于一个保护域。具有相同权限、但来自不同代码源的类,属于不同的保护域。

每个Java类都运行在由代码源决定的恰当的保护域里。运行在安全管理器之下的任何代码,在执行任何安全相关的操作时,都必须被授予特定的权限,如读或者写一个文件时必须要有执行文件读或者写的权限。通过使用AccessController.doPrivileged()方法,特权代码可以代表无特权的调用者,访问特权资源,这是很有必要的。例如,当一个系统工具程序需要代表用户打开一个字体文件用来显示一个文档,但是应用程序本身缺乏权限做这样的事的时候。为了执行该操作,系统工具程序会使用它的全部特权来获取这个字体,而忽略调用者的特权。特权代码运行在与代码源相关的所有特权保护域里。这些特权往往超出了执行特权操作的需要。理想的情况下,代码应该被授予恰好满足其完成操作所需特权的最小集合。

指南19中描述了另外一种用来消除多余特权的方法。

违规代码示例

下面的违规代码示例显示了一个库方法,通过使用包装器方法performActionOnFile()来允许调用者执行授权操作(读文件)。

private FileInputStream openFile() {
 final FileInputStream f[] = { null };

 AccessController.doPrivileged(new PrivilegedAction() {
  public Object run() {
   try {
    f[0] = new FileInputStream("file");
   } catch(FileNotFoundException fnf) {
    // Forward to handler
   }
   return null;
  }
 }); 
 return f[0];
}

// Wrapper method
public void performActionOnFile() {
 try (FileInputStream f = openFile()){
  // Perform operation
 } catch (Throwable t) {
  // Handle exception
 }
}```
在这个例子中,对可信代码授予的特权超出了读取一个文件的真实需要,即便是需要读取文件,也只需要为doPrivileged()代码块授权。因此,这段代码为代码块提供了多余的特权,从而违反了最小特权原则。

####合规解决方案
双参数形式的doPrivileged()方法从调用者那里接受一个作为第二个参数传递的AccessControlContext对象,并将所包含代码的特权限制在保护域特权和上下文权限的交集中。因此,当调用者只希望授予代码读取文件权限时,可以提供一个只有文件读取权限的上下文。

一个被适当授予文件读取权限的AccessControlContext,可以作为一个内部类:

private FileInputStream openFile(AccessControlContext context) {
 if (context == null) {
  throw new SecurityException("Missing AccessControlContext");
 }

 final FileInputStream f[] = { null };
 AccessController.doPrivileged(
  new PrivilegedAction() {
   public Object run() {
    try {
     f[0] = new FileInputStream("file");
    } catch (FileNotFoundException fnf) {
     // Forward to handler
    }
    return null;
   }
  },
  // Restrict the privileges by passing the context argument
  context);
  return f[0];
}

private static class FileAccessControlContext {
 public static final AccessControlContext INSTANCE;
 static {
  Permission perm = new java.io.FilePermission("file", "read");
  PermissionCollection perms = perm.newPermissionCollection();
  perms.add(perm);
  INSTANCE = new AccessControlContext(new ProtectionDomain[] {
   new ProtectionDomain(null, perms)});
 }
}

// Wrapper method
public void performActionOnFile() {
 try {
  final FileInputStream f =
   // Grant only open-for-reading privileges
   openFile(FileAccessControlContext.INSTANCE);
   // Perform action
 } catch (Throwable t) {
  // Handle exception
 }
}`
如果调用者缺乏创建一个适当的AccessControlContext的权限,那么可以通过请求AccessController.getContext()来创建一个这样的实例。

适用性

未能遵循最小特权原则可能导致不可信、未授权的代码执行意想不到的特权操作。然而,过细地限制特权会增加程序复杂性。这些增加的复杂性和相应减少的可维护性必须同安全改进做出利弊权衡。

相关文章
|
2天前
|
存储 安全 Java
12条通用编程原则✨全面提升Java编码规范性、可读性及性能表现
12条通用编程原则✨全面提升Java编码规范性、可读性及性能表现
|
2天前
|
Java Linux C语言
一步带你了解java程序逻辑控制
一步带你了解java程序逻辑控制
10 2
|
2天前
|
Java 数据安全/隐私保护
java中程序控制的典例
java中程序控制的典例
9 1
|
3天前
|
存储 Java 数据库连接
使用Java开发桌面应用程序
使用Java开发桌面应用程序
14 0
|
3天前
|
关系型数据库 MySQL Java
通过使用阿里云服务器,搭建Java程序的运行环境
通过使用阿里云服务器,搭建Java程序的运行环境
|
10天前
|
存储 网络协议 Java
本地MinIO存储服务通过Java程序结合cpolar实现远程连接上传文件
本地MinIO存储服务通过Java程序结合cpolar实现远程连接上传文件
|
11天前
|
Java Spring
Java 效率编码 必备插件 Lombok 让代码更优雅
该内容是一个关于Lombok插件的教程摘要:介绍了Lombok用于减少Java开发中的模板代码,提升效率;讲解了如何在IntelliJ IDEA中安装Lombok插件,以及在pom.xml中添加依赖;并提到了@Data注解能自动生成getter/setter、equals、hashCode和toString方法,@Slf4j注解自动处理日志,@Builder用于构建对象,以及@AllArgsConstructor和@NoArgsConstructor注解生成构造函数。还鼓励探索更多Lombok的注解用法。
|
12天前
|
存储 Java 开发工具
【Java探索之旅】用面向对象的思维构建程序世界
【Java探索之旅】用面向对象的思维构建程序世界
11 0
|
19小时前
|
Java
Java一分钟:线程协作:wait(), notify(), notifyAll()
【5月更文挑战第11天】本文介绍了Java多线程编程中的`wait()`, `notify()`, `notifyAll()`方法,它们用于线程间通信和同步。这些方法在`synchronized`代码块中使用,控制线程执行和资源访问。文章讨论了常见问题,如死锁、未捕获异常、同步使用错误及通知错误,并提供了生产者-消费者模型的示例代码,强调理解并正确使用这些方法对实现线程协作的重要性。
9 3
|
19小时前
|
安全 算法 Java
Java一分钟:线程同步:synchronized关键字
【5月更文挑战第11天】Java中的`synchronized`关键字用于线程同步,防止竞态条件,确保数据一致性。本文介绍了其工作原理、常见问题及避免策略。同步方法和同步代码块是两种使用形式,需注意避免死锁、过度使用导致的性能影响以及理解锁的可重入性和升级降级机制。示例展示了同步方法和代码块的运用,以及如何避免死锁。正确使用`synchronized`是编写多线程安全代码的核心。
10 2