《Java安全编码标准》一2.8 IDS07-J不要向Runtime.exec()?方法传递非受信、未净化的数据

简介: 本节书摘来自华章出版社《Java安全编码标准》一书中的第2章,第2.8节,作者 (美)Fred Long,Dhruv Mohindra,Robert C. Seacord,Dean F. Sutherland,David Svoboda,更多章节内容可以访问云栖社区“华章计算机”公众号查看

2.8 IDS07-J不要向Runtime.exec()?方法传递非受信、未净化的数据

外部程序通常被系统调用来完成某种需要的功能。这是一种重用的形式,也被认为是一种简单基于组件的软件工程方法。在应用没有净化非受信的输入并且在执行外部程序时使用这种数据,就会导致产生命令和参数注入漏洞。
每一个Java应用都有一个唯一的Runtime类的实例,通过它可以提供一个应用和应用运行环境的接口。当前的Runtime对象可以通过Runtime.getRuntime()方法获得。Runtime.getRuntime()的语义定义并不严格,所以最好仅仅使用它的那些必要的行为,然而,在通常情况下,它应当被命令直接调用,而不应通过shell来调用。如果需要使用shell来调用它,可以在POSIX中使用/bin/sh或者在Windows平台中使用cmd.exe的方式。作为exec()的另一种形式,它会使用StringTokenizer来分隔从命令行读入的字符串。在Windows平台中,在处理这些符号时,它们会被连接成一个单一的参数字符串。
因而,除非显式地调用命令行解释器,否则是不会产生命令行注入攻击的。然而,当参数中包含那些以空格、双引号或者其他以- /开头的用来表示分支的字符时,就可能发生参数注入攻击。
这条规则是规则IDS00-J的特例。任何源于程序受信边界之外的字符串数据,在当前平台作为命令来执行之前,都必须经过净化。

2.8.1 不符合规则的代码示例(Windows)

该代码示例使用dir命令列出目录列表。这是通过Runtime.exec()?方法调用Windows的dir命令来实现的。

class DirList {
??public static void main(String[] args) throws Exception {
????String dir = System.getProperty("dir");
????Runtime rt = Runtime.getRuntime();
????Process proc = rt.exec("cmd.exe /C dir " + dir);
????int result = proc.waitFor();
????if (result != 0) {
??????System.out.println("process error: " + result);
????}
????InputStream in = (result == 0) ? proc.getInputStream() :
?????????????????????????????????????proc.getErrorStream();
????int c;
????while ((c = in.read()) != -1) {
??????System.out.print((char) c);
????}
??}
}

因为Runtime.exec()方法接受源于运行环境的未经净化的数据,所以这些代码会引起命令注入攻击。
攻击者可以通过以下命令利用该程序:

java -Ddir='dummy & echo bad' Java

该命令实际上执行的是两条命令:

cmd.exe /C dir dummy & echo bad

第一条命令会列出并不存在的dummy文件夹,并且在控制台上输出bad。

2.8.2 不符合规则的代码示例(POSIX)

这一个代码示例的功能与2.8.1节介绍的类似,只是它使用了POSIX中的ls命令。与Windows版本的唯一区别在于传入Runtime.exec()的参数。

class DirList {
??public static void main(String[] args) throws Exception {
????String dir = System.getProperty("dir");
????Runtime rt = Runtime.getRuntime();
????Process proc = rt.exec(new String[] {"sh", "-c", "ls " + dir});
????int result = proc.waitFor();
????if (result != 0) {
??????System.out.println("process error: " + result);
????}
????InputStream in = (result == 0) ? proc.getInputStream() :
?????????????????????????????????????proc.getErrorStream();
????int c;
????while ((c = in.read()) != -1) {
??????System.out.print((char) c);
????}
??}
}

攻击者可以通过使用与上例中一样的命令取得同样的效果。这个命令实际执行的是:

sh -c 'ls dummy & echo bad'

2.8.3 符合规则的方案(净化)

符合规则的方案会对非受信的用户输入进行净化,这种净化只允许一小组列入白名单的字符出现在参数中,并传给Runtime.exec()方法,其他所有的字符都会被排除掉。

// ...
if (!Pattern.matches("[0-9A-Za-z@.]+", dir)) {
??// Handle error
}
// ...

尽管这是一个符合规则的方案,这个净化方案会拒绝合法的目录。同时,因为命令行解释器调用是依赖于系统的,所以很难有一个方案可以应付所有Java程序可以运行的平台上的命令行注入。

2.8.4 符合规则的方案(限定用户选择)

这个符合规则的方案通过只向Runtime.exec()方法输入那些受信的字符串来防止命令注入。当用户可以控制使用哪一个字符串时,用户就可以不向Runtime.exec()方法直接提供字符串数据。

// ...
String dir = null;
// only allow integer choices
int number = Integer.parseInt(System.getproperty("dir"));?
switch (number) {
??case 1:?
????dir = "data1"
????break; // Option 1
??case 2:?
????dir = "data2"
????break; // Option 2
??default: // invalid
????break;?
}
if (dir == null) {
??// handle error
}

这个方案将可能罗列出的目录直接写出来了。
如果有许多可用目录这个方案很快会变得不好管理。一个更富可伸缩性的方案是从一个属性文件中读取所有允许的目录至java.util.Properties对象中。

2.8.5 符合规则的方案(避免使用Runtime.exec())

当通过执行系统命令可以完成的任务可以用其他方式完成时,最好避免用执行系统命令的方式。这个符合规则的方案使用File.list()方法来提供目录列表,从而消除了命令或参数注入攻击发生的可能性。

import java.io.File;

class DirList {
??public static void main(String[] args) throws Exception {
????File dir = new File(System.getProperty("dir"));
????if (!dir.isDirectory()) {
??????System.out.println("Not a directory");
????} else {
??????for (String file : dir.list()) {
????????System.out.println(file);
??????}
????}
??}
}

2.8.6 风险评估

向?Runtime.exec()?方法传递非受信的、未经净化的数据会导致命令和参数注入攻击。
image

相关漏洞
image

2.8.7 相关规范

image

2.8.8 参考书目

image

相关文章
|
18天前
|
存储 JSON Java
《从头开始学java,一天一个知识点》之:方法定义与参数传递机制
**你是否也经历过这些崩溃瞬间?** - 看了三天教程,连`i++`和`++i`的区别都说不清 - 面试时被追问"`a==b`和`equals()`的区别",大脑突然空白 - 写出的代码总是莫名报NPE,却不知道问题出在哪个运算符 🚀 这个系列就是为你打造的Java「速效救心丸」!我们承诺:每天1分钟,地铁通勤、午休间隙即可完成学习;直击痛点,只讲高频考点和实际开发中的「坑位」;拒绝臃肿,没有冗长概念堆砌,每篇都有可运行的代码标本。上篇:《输入与输出:Scanner与System类》 | 下篇剧透:《方法重载与可变参数》。
46 25
|
12天前
|
安全 IDE Java
重学Java基础篇—Java Object类常用方法深度解析
Java中,Object类作为所有类的超类,提供了多个核心方法以支持对象的基本行为。其中,`toString()`用于对象的字符串表示,重写时应包含关键信息;`equals()`与`hashCode()`需成对重写,确保对象等价判断的一致性;`getClass()`用于运行时类型识别;`clone()`实现对象复制,需区分浅拷贝与深拷贝;`wait()/notify()`支持线程协作。此外,`finalize()`已过时,建议使用更安全的资源管理方式。合理运用这些方法,并遵循最佳实践,可提升代码质量与健壮性。
21 1
|
16天前
|
前端开发 Cloud Native Java
Java||Springboot读取本地目录的文件和文件结构,读取服务器文档目录数据供前端渲染的API实现
博客不应该只有代码和解决方案,重点应该在于给出解决方案的同时分享思维模式,只有思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
Java||Springboot读取本地目录的文件和文件结构,读取服务器文档目录数据供前端渲染的API实现
|
26天前
|
运维 Java 程序员
Java中的异常处理方法
本文深入剖析Java异常处理机制,介绍可检查异常、运行时异常和错误的区别与处理方式。通过最佳实践方法,如使用合适的异常类型、声明精确异常、try-with-resources语句块、记录异常信息等,帮助开发者提高代码的可靠性、可读性和可维护性。良好的异常处理能保证程序稳定运行,避免资源泄漏和潜在问题。
|
26天前
|
传感器 监控 Java
Java代码结构解析:类、方法、主函数(1分钟解剖室)
### Java代码结构简介 掌握Java代码结构如同拥有程序世界的建筑蓝图,类、方法和主函数构成“黄金三角”。类是独立的容器,承载成员变量和方法;方法实现特定功能,参数控制输入环境;主函数是程序入口。常见错误包括类名与文件名不匹配、忘记static修饰符和花括号未闭合。通过实战案例学习电商系统、游戏角色控制和物联网设备监控,理解类的作用、方法类型和主函数任务,避免典型错误,逐步提升编程能力。 **脑图速记法**:类如太空站,方法即舱段;main是发射台,static不能换;文件名对仗,括号要成双;参数是坐标,void不返航。
47 5
|
1月前
|
存储 安全 算法
Java容器及其常用方法汇总
Java Collections框架提供了丰富的接口和实现类,用于管理和操作集合数据。
Java容器及其常用方法汇总
|
1月前
|
数据采集 JSON Java
Java爬虫获取微店快递费用item_fee API接口数据实现
本文介绍如何使用Java开发爬虫程序,通过微店API接口获取商品快递费用(item_fee)数据。主要内容包括:微店API接口的使用方法、Java爬虫技术背景、需求分析和技术选型。具体实现步骤为:发送HTTP请求获取数据、解析JSON格式的响应并提取快递费用信息,最后将结果存储到本地文件中。文中还提供了完整的代码示例,并提醒开发者注意授权令牌、接口频率限制及数据合法性等问题。
|
1月前
|
Java API 数据处理
深潜数据海洋:Java文件读写全面解析与实战指南
通过本文的详细解析与实战示例,您可以系统地掌握Java中各种文件读写操作,从基本的读写到高效的NIO操作,再到文件复制、移动和删除。希望这些内容能够帮助您在实际项目中处理文件数据,提高开发效率和代码质量。
37 4
|
1月前
|
Java API
java.time常用方法汇总
`java.time` API 是从 Java 8 开始引入的时间日期处理库,旨在替代老旧的 `java.util.Date` 和 `Calendar`。它提供了更简洁、强大和灵活的方式处理日期、时间、时区及时间间隔,支持全球化和时间计算需求。API 包含获取当前时间、创建指定时间、解析和格式化字符串、进行加减运算、比较时间、获取年月日时分秒、计算时间间隔、时区转换以及判断闰年等功能。示例代码展示了如何使用这些功能,极大简化了开发中的时间处理任务。
|
Java C语言 C++
Java 的数据类型划分(数据类型划分)| 学习笔记
快速学习 Java 的数据类型划分(数据类型划分)
143 0
Java 的数据类型划分(数据类型划分)| 学习笔记