《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()来创建一个这样的实例。

适用性

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

相关文章
|
3月前
|
Java
Java开发实现图片URL地址检验,如何编码?
【10月更文挑战第14天】Java开发实现图片URL地址检验,如何编码?
100 4
|
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
|
2月前
|
IDE Java 编译器
开发 Java 程序一定要安装 JDK 吗
开发Java程序通常需要安装JDK(Java Development Kit),因为它包含了编译、运行和调试Java程序所需的各种工具和环境。不过,某些集成开发环境(IDE)可能内置了JDK,或可使用在线Java编辑器,无需单独安装。
83 1
|
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
|
3月前
|
Java Maven 数据安全/隐私保护
如何实现Java打包程序的加密代码混淆,避免被反编译?
【10月更文挑战第15天】如何实现Java打包程序的加密代码混淆,避免被反编译?
319 2
|
3月前
|
安全 Java Linux
java程序设置开机自启
java程序设置开机自启
173 1
|
3月前
|
运维 Java Linux
【运维基础知识】Linux服务器下手写启停Java程序脚本start.sh stop.sh及详细说明
### 启动Java程序脚本 `start.sh` 此脚本用于启动一个Java程序,设置JVM字符集为GBK,最大堆内存为3000M,并将程序的日志输出到`output.log`文件中,同时在后台运行。 ### 停止Java程序脚本 `stop.sh` 此脚本用于停止指定名称的服务(如`QuoteServer`),通过查找并终止该服务的Java进程,输出操作结果以确认是否成功。
87 1