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

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 本节书摘来自华章出版社《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

相关文章
|
28天前
|
SQL 安全 Java
安全问题已经成为软件开发中不可忽视的重要议题。对于使用Java语言开发的应用程序来说,安全性更是至关重要
在当今网络环境下,Java应用的安全性至关重要。本文深入探讨了Java安全编程的最佳实践,包括代码审查、输入验证、输出编码、访问控制和加密技术等,帮助开发者构建安全可靠的应用。通过掌握相关技术和工具,开发者可以有效防范安全威胁,确保应用的安全性。
48 4
|
2月前
|
存储 网络协议 前端开发
在 Java 中如何完全验证 URL
在 Java 中如何完全验证 URL
87 8
|
2月前
|
安全 Java 编译器
Java 泛型深入解析:类型安全与灵活性的平衡
Java 泛型通过参数化类型实现了代码重用和类型安全,提升了代码的可读性和灵活性。本文深入探讨了泛型的基本原理、常见用法及局限性,包括泛型类、方法和接口的使用,以及上界和下界通配符等高级特性。通过理解和运用这些技巧,开发者可以编写更健壮和通用的代码。
|
3月前
|
安全 Java API
java安全特性
java安全特性
29 8
|
3月前
|
安全 Java API
【性能与安全的双重飞跃】JDK 22外部函数与内存API:JNI的继任者,引领Java新潮流!
【9月更文挑战第7天】JDK 22外部函数与内存API的发布,标志着Java在性能与安全性方面实现了双重飞跃。作为JNI的继任者,这一新特性不仅简化了Java与本地代码的交互过程,还提升了程序的性能和安全性。我们有理由相信,在外部函数与内存API的引领下,Java将开启一个全新的编程时代,为开发者们带来更加高效、更加安全的编程体验。让我们共同期待Java在未来的辉煌成就!
72 11
|
3月前
|
安全 Java API
【本地与Java无缝对接】JDK 22外部函数和内存API:JNI终结者,性能与安全双提升!
【9月更文挑战第6天】JDK 22的外部函数和内存API无疑是Java编程语言发展史上的一个重要里程碑。它不仅解决了JNI的诸多局限和挑战,还为Java与本地代码的互操作提供了更加高效、安全和简洁的解决方案。随着FFM API的逐渐成熟和完善,我们有理由相信,Java将在更多领域展现出其强大的生命力和竞争力。让我们共同期待Java编程新纪元的到来!
108 11
|
20天前
|
Java 开发者
Java多线程编程中的常见误区与最佳实践####
本文深入剖析了Java多线程编程中开发者常遇到的几个典型误区,如对`start()`与`run()`方法的混淆使用、忽视线程安全问题、错误处理未同步的共享变量等,并针对这些问题提出了具体的解决方案和最佳实践。通过实例代码对比,直观展示了正确与错误的实现方式,旨在帮助读者构建更加健壮、高效的多线程应用程序。 ####
|
11天前
|
缓存 Java 开发者
Java多线程编程的陷阱与最佳实践####
本文深入探讨了Java多线程编程中常见的陷阱,如竞态条件、死锁和内存一致性错误,并提供了实用的避免策略。通过分析典型错误案例,本文旨在帮助开发者更好地理解和掌握多线程环境下的编程技巧,从而提升并发程序的稳定性和性能。 ####
|
6天前
|
监控 Java 开发者
深入理解Java中的线程池实现原理及其性能优化####
本文旨在揭示Java中线程池的核心工作机制,通过剖析其背后的设计思想与实现细节,为读者提供一份详尽的线程池性能优化指南。不同于传统的技术教程,本文将采用一种互动式探索的方式,带领大家从理论到实践,逐步揭开线程池高效管理线程资源的奥秘。无论你是Java并发编程的初学者,还是寻求性能调优技巧的资深开发者,都能在本文中找到有价值的内容。 ####
|
11天前
|
安全 Java 开发者
Java中的多线程编程:从基础到实践
本文深入探讨了Java多线程编程的核心概念和实践技巧,旨在帮助读者理解多线程的工作原理,掌握线程的创建、管理和同步机制。通过具体示例和最佳实践,本文展示了如何在Java应用中有效地利用多线程技术,提高程序性能和响应速度。
41 1