《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

相关文章
|
25天前
|
人工智能 监控 安全
Java智慧工地(源码):数字化管理提升施工安全与质量
随着科技的发展,智慧工地已成为建筑行业转型升级的重要手段。依托智能感知设备和云物互联技术,智慧工地为工程管理带来了革命性的变革,实现了项目管理的简单化、远程化和智能化。
37 4
|
4月前
|
SQL 安全 Java
Java 异常处理:筑牢程序稳定性的 “安全网”
本文深入探讨Java异常处理,涵盖异常的基础分类、处理机制及最佳实践。从`Error`与`Exception`的区分,到`try-catch-finally`和`throws`的运用,再到自定义异常的设计,全面解析如何有效管理程序中的异常情况,提升代码的健壮性和可维护性。通过实例代码,帮助开发者掌握异常处理技巧,确保程序稳定运行。
79 2
|
4月前
|
SQL 安全 Java
安全问题已经成为软件开发中不可忽视的重要议题。对于使用Java语言开发的应用程序来说,安全性更是至关重要
在当今网络环境下,Java应用的安全性至关重要。本文深入探讨了Java安全编程的最佳实践,包括代码审查、输入验证、输出编码、访问控制和加密技术等,帮助开发者构建安全可靠的应用。通过掌握相关技术和工具,开发者可以有效防范安全威胁,确保应用的安全性。
81 4
|
5月前
|
存储 网络协议 前端开发
在 Java 中如何完全验证 URL
在 Java 中如何完全验证 URL
132 8
|
5月前
|
安全 Java 编译器
Java 泛型深入解析:类型安全与灵活性的平衡
Java 泛型通过参数化类型实现了代码重用和类型安全,提升了代码的可读性和灵活性。本文深入探讨了泛型的基本原理、常见用法及局限性,包括泛型类、方法和接口的使用,以及上界和下界通配符等高级特性。通过理解和运用这些技巧,开发者可以编写更健壮和通用的代码。
|
6月前
|
安全 Java API
java安全特性
java安全特性
45 8
|
9天前
|
存储 监控 Java
【Java并发】【线程池】带你从0-1入门线程池
欢迎来到我的技术博客!我是一名热爱编程的开发者,梦想是编写高端CRUD应用。2025年我正在沉淀中,博客更新速度加快,期待与你一起成长。 线程池是一种复用线程资源的机制,通过预先创建一定数量的线程并管理其生命周期,避免频繁创建/销毁线程带来的性能开销。它解决了线程创建成本高、资源耗尽风险、响应速度慢和任务执行缺乏管理等问题。
120 60
【Java并发】【线程池】带你从0-1入门线程池
|
5天前
|
Java 调度
【源码】【Java并发】【线程池】邀请您从0-1阅读ThreadPoolExecutor源码
当我们创建一个`ThreadPoolExecutor`的时候,你是否会好奇🤔,它到底发生了什么?比如:我传的拒绝策略、线程工厂是啥时候被使用的? 核心线程数是个啥?最大线程数和它又有什么关系?线程池,它是怎么调度,我们传入的线程?...不要着急,小手手点上关注、点赞、收藏。主播马上从源码的角度带你们探索神秘线程池的世界...
51 0
【源码】【Java并发】【线程池】邀请您从0-1阅读ThreadPoolExecutor源码
|
21天前
|
Java 程序员 开发者
Java社招面试题:一个线程运行时发生异常会怎样?
大家好,我是小米。今天分享一个经典的 Java 面试题:线程运行时发生异常,程序会怎样处理?此问题考察 Java 线程和异常处理机制的理解。线程发生异常,默认会导致线程终止,但可以通过 try-catch 捕获并处理,避免影响其他线程。未捕获的异常可通过 Thread.UncaughtExceptionHandler 处理。线程池中的异常会被自动处理,不影响任务执行。希望这篇文章能帮助你深入理解 Java 线程异常处理机制,为面试做好准备。如果你觉得有帮助,欢迎收藏、转发!
87 14
|
24天前
|
安全 Java 程序员
Java 面试必问!线程构造方法和静态块的执行线程到底是谁?
大家好,我是小米。今天聊聊Java多线程面试题:线程类的构造方法和静态块是由哪个线程调用的?构造方法由创建线程实例的主线程调用,静态块在类加载时由主线程调用。理解这些细节有助于掌握Java多线程机制。下期再见! 简介: 本文通过一个常见的Java多线程面试题,详细讲解了线程类的构造方法和静态块是由哪个线程调用的。构造方法由创建线程实例的主线程调用,静态块在类加载时由主线程调用。理解这些细节对掌握Java多线程编程至关重要。
53 13

热门文章

最新文章