《Java安全编码标准》一2.3 IDS02-J在验证之前标准化路径名

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
云解析DNS,个人版 1个月
简介: 本节书摘来自华章出版社《Java安全编码标准》一书中的第2章,第2.3节,作者 (美)Fred Long,Dhruv Mohindra,Robert C. Seacord,Dean F. Sutherland,David Svoboda,更多章节内容可以访问云栖社区“华章计算机”公众号查看

2.3 IDS02-J在验证之前标准化路径名

根据Java API[API 2006]文档对java.io.File类的描述:
一个路径名,不管是抽象的还是字符串形式的,可以是相对路径也可以是绝对路径。使用绝对路径名,因为在定位一个路径表示的文件时,已经不需要其他信息了,因而可以认为是完整的。相比之下,一个相对路径名必须要增加其他的路径信息才能进行解释。
绝对路径名或者相对路径名会包含文件链接,比如符号(软)链接、硬链接、快捷方式、影子、别名和联名。这些文件链接在文件验证操作进行之前必须完全解析。例如,一个命名为trace的符号链接最终可以是指向路径名/home/system/trace。该路径名可能同时会包含特殊的文件名,这些文件名会让验证变得困难。
1)“.”指目录本身。
2)在目录内,一个特殊的文件名“..”指该目录的上一级目录。
除了这些特殊的问题之外,还有很多是和操作系统有关的,并且是和文件系统有关的命名转换,从而让验证变得困难。
标准化文件名比验证路径名要容易得多。很多路径名会指向一个目录或文件。此外,考虑到路径名指向的目录或文件,一个路径名的文本表示会产生很少或没有信息。结果是,所有的路径名必须被完整解析或者在验证之前进行标准化(canonicalized)。
验证可能是必要的,例如,当需要限定用户访问在某个特定目录下的文件,或者需要基于文件名或者路径名的时候,需要做出对应的安全决策。通常,这些限制被攻击者利用目录遍历(directory traversal)或者等价路径(path equivalence)这样的方式规避而产生漏洞。一个目录遍历漏洞会让I/O操作转到另一个特定的操作目录中去。当攻击者提供一个不同但是有等价名称的资源,并绕过安全检查的时候,就会由此产生一个路径等价漏洞。
标准化包括内在的竞态窗口,这个窗口在程序获得标准路径名的时间和打开文件的时间之间产生。在验证标准化的路径名的时候,文件系统可能已经被修改,并且标准化的路径名不再指向原有的合法文件。幸运的是,可以很容易消除这些竞态。标准化的路径名可以用来确定引用的文件名是否在一个安全的目录中(参考FIO00-J可以得到更多的信息)。如果引用的文件在一个安全的目录之内,根据定义,攻击者就不能篡改它也不能利用这些竞态条件。
该规则是规则IDS01-J的一个实例。

2.3.1 不符合规则的代码示例

以下不符合规则的代码示例可以从命令行参数接收文件路径,并使用File.getAbsolutePath()方法来获得绝对路径。它同时会使用?isInSecureDir()方法,这个方法在规则FIO00-J中进行定义,它可以用来保证文件在一个安全的目录中。然而,它并不能解析文件链接,也不能消除这些同类的错误。

public static void main(String[] args) {
??File f = new File(System.getProperty("user.home") +
??System.getProperty("file.separator") + args[0]);
??String absPath = f.getAbsolutePath();

??if (!isInSecureDir(Paths.get(absPath))) {
????throw new IllegalArgumentException();
??}
??if (!validate(absPath)) { // Validation
????throw new IllegalArgumentException();
??}
}

应用需要限制用户操作那些位于home目录之外的文件。validate()方法可以保证路径名在这个目录当中,但这个方法可以很容易被绕过。例如,用户可以在他的home目录中创建一个链接,这个链接指向位于home目录之外的目录或文件。当validate()方法处理这个链接的路径名的时候,仍然还认为它是在home目录中,结果是可以顺利通过验证,但在实际的操作中,它会操作,位于home目录之外的最终路径指向。
注意,在Windows和Macintosh平台中,File.getAbsolutePath()?方法可以解析符号链接、别名和快捷方式。尽管如此,在Java语言规范中却不能保证这样的行为在所有的平台上都有效,或者在未来的实现中均会如此。

2.3.2 符合规则的方案(getCanonicalPath())

该符合规则的解决方案使用?getCanonicalPath()?方法,这个方法是在Java 2中引入的,因为它能在所有的平台上对所有别名、快捷方式以及符号链接进行一致解析。那些特殊文件名如..同样被去除了,所以在执行验证之前,输入就被缩减成一种标准化形式。当存在validate()方法时,攻击者无法通过使用../序列进入特定的目录。

public static void main(String[] args) throws IOException {
??File f = new File(System.getProperty("user.home") +
??System.getProperty("file.separator")+ args[0]);
??String canonicalPath = f.getCanonicalPath();

??if (!isInSecureDir(Paths.get(canonicalPath))) {
????throw new IllegalArgumentException();
??}
??if (!validate(canonicalPath)) { // Validation
???throw new IllegalArgumentException();
??}
}

当在applet中使用时,getCanonicalPath()方法会抛出安全异常,因而会泄露太多关于宿主机的信息。getCanonicalFile()方法和getCanonicalPath()类似,但它会返回一个新的File对象而不是一个String。

2.3.3 符合规则的方案(安全管理器)

处理这类问题的一个综合方法是,给应用赋予相应的权限,而这些权限只对特定目录下的文件有效 ,比如用户的home目录。该方案只需要在程序的安全管理策略文件中指明程序的绝对路径,并且将java.io.FilePermission?以及读写权限赋予目录${user.home}/*?的路径。

grant codeBase "file:/home/programpath/" {
??permission java.io.FilePermission "${user.home}/*", "read, write";
};

该方案要求用户的home目录是一个安全目录,这会在规则FIO00-J中描述。

2.3.4 不符合规则的代码示例一

这段代码示例允许用户指定所操作的文件名的绝对路径,改用包含../序列的参数来指定位于特定目录之外的文件(在这个示例中是/img路径),从而违反了该程序的安全策略。

FileOutputStream fis =
??new FileOutputStream(new File("/img/" + args[0]));
// ...

2.3.5 不符合规则的代码示例二

该代码示例希望解决使用File.getCanonicalPath()?方法的问题,该方法完全解析参数并构造出标准化路径。例如,路径/img/../etc/passwd可以解析为/etc/passwd。没有经过验证的标准化是不够的,因为攻击者可以使用指定目录之外的特定文件。

File f = new File("/img/" + args[0]);
String canonicalPath = f.getCanonicalPath();
FileOutputStream fis = new FileOutputStream(f);
// ...

2.3.6 符合规则的方案

该方案从非受信的用户输入中获取文件名,对其进行标准化,然后基于起始路径名列表对其进行验证。当验证成功时,才会操作指定文件,也就是说,仅当该文件是在/img/java目录下的file1.txt文件或者file2.txt文件中的一个时才行。

File f = new File("/img/" + args[0]);
String canonicalPath = f.getCanonicalPath();

if (!canonicalPath.equals("/img/java/file1.txt") &&
????!canonicalPath.equals("/img/java/file2.txt")) {
???// Invalid file; handle error
}

FileInputStream fis = new FileInputStream(f);

/img/java目录必须是一个安全目录,不存在任何竞态条件。

2.3.7 符合规则的方案(安全管理器)

该方案赋予应用相应的权限读取指定目录或文件。比如,读权限的赋予,可以通过在安全策略文件中为该程序指定一个绝对路径名,并设置java.io.FilePermission为一个文件或目录的标准化绝对路径,然后将动作设为read。

// All files in /img/java can be read
grant codeBase "file:/home/programpath/" {
??permission java.io.FilePermission "/img/java", "read";
};

2.3.8 风险评估
当使用来自非受信源的路径名时,如果不首先进行标准化,然后进行验证,那么会导致目录遍历和路径等价漏洞。
image

相关漏洞 CVE-2005-0789 描述了在LimeWire 3.9.6~4.6.0节中的目录遍历漏洞,它的存在让远程攻击者可以通过在请求中通过..路径名读取任意文件。
CVE-2008-5518 描述了多种目录遍历漏洞,在Apache Geronimo应用服务器2.1到2.1.3 的Windows版本的Web管理员控制台中,它允许远程攻击者向任意目录上传文件。

2.3.9 相关规范

image

2.3.10 参考书目

image

相关文章
|
13天前
|
存储 Java API
【Azure 存储服务】Java Storage SDK 调用 uploadWithResponse 代码示例(询问ChatGTP得代码原型后人力验证)
【Azure 存储服务】Java Storage SDK 调用 uploadWithResponse 代码示例(询问ChatGTP得代码原型后人力验证)
|
1天前
|
安全 Java API
【性能与安全的双重飞跃】JDK 22外部函数与内存API:JNI的继任者,引领Java新潮流!
【9月更文挑战第7天】JDK 22外部函数与内存API的发布,标志着Java在性能与安全性方面实现了双重飞跃。作为JNI的继任者,这一新特性不仅简化了Java与本地代码的交互过程,还提升了程序的性能和安全性。我们有理由相信,在外部函数与内存API的引领下,Java将开启一个全新的编程时代,为开发者们带来更加高效、更加安全的编程体验。让我们共同期待Java在未来的辉煌成就!
19 11
|
2天前
|
安全 Java API
【本地与Java无缝对接】JDK 22外部函数和内存API:JNI终结者,性能与安全双提升!
【9月更文挑战第6天】JDK 22的外部函数和内存API无疑是Java编程语言发展史上的一个重要里程碑。它不仅解决了JNI的诸多局限和挑战,还为Java与本地代码的互操作提供了更加高效、安全和简洁的解决方案。随着FFM API的逐渐成熟和完善,我们有理由相信,Java将在更多领域展现出其强大的生命力和竞争力。让我们共同期待Java编程新纪元的到来!
21 11
|
17天前
|
安全 算法 Java
java系列之~~网络通信安全 非对称加密算法的介绍说明
这篇文章介绍了非对称加密算法,包括其定义、加密解密过程、数字签名功能,以及与对称加密算法的比较,并解释了非对称加密在网络安全中的应用,特别是在公钥基础设施和信任网络中的重要性。
|
18天前
|
Java
【Java集合类面试十二】、HashMap为什么线程不安全?
HashMap在并发环境下执行put操作可能导致循环链表的形成,进而引起死循环,因而它是线程不安全的。
|
7天前
|
前端开发 开发者 安全
JSF支付功能大揭秘:探索如何在Java世界中实现安全无缝的在线支付体验
【8月更文挑战第31天】在电子商务和在线交易日益普及的今天,实现在线支付功能已成为许多Web应用的必备需求。JavaServer Faces (JSF) 作为一种流行的Java Web框架,提供了丰富的组件和工具来构建用户界面,包括与支付网关集成以实现在线支付。支付网关是处理信用卡和借记卡支付的系统,在商家和银行之间起到桥梁作用。本文将探讨如何使用JSF与支付网关集成,以及实现在线支付功能时需要考虑的关键点
18 0
|
15天前
|
存储 Java
如何在 Java 中验证 ArrayList?
【8月更文挑战第23天】
23 0
|
4天前
|
监控 Java 调度
【Java学习】多线程&JUC万字超详解
本文详细介绍了多线程的概念和三种实现方式,还有一些常见的成员方法,CPU的调动方式,多线程的生命周期,还有线程安全问题,锁和死锁的概念,以及等待唤醒机制,阻塞队列,多线程的六种状态,线程池等
26 6
【Java学习】多线程&JUC万字超详解
|
3天前
|
存储 Java 程序员
优化Java多线程应用:是创建Thread对象直接调用start()方法?还是用个变量调用?
这篇文章探讨了Java中两种创建和启动线程的方法,并分析了它们的区别。作者建议直接调用 `Thread` 对象的 `start()` 方法,而非保持强引用,以避免内存泄漏、简化线程生命周期管理,并减少不必要的线程控制。文章详细解释了这种方法在使用 `ThreadLocal` 时的优势,并提供了代码示例。作者洛小豆,文章来源于稀土掘金。
|
22小时前
|
Java 开发者
Java中的多线程编程基础与实战
【9月更文挑战第6天】本文将通过深入浅出的方式,带领读者了解并掌握Java中的多线程编程。我们将从基础概念出发,逐步深入到代码实践,最后探讨多线程在实际应用中的优势和注意事项。无论你是初学者还是有一定经验的开发者,这篇文章都能让你对Java多线程有更全面的认识。
8 1
下一篇
DDNS