《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

相关文章
|
4天前
|
Java 关系型数据库 MySQL
Elasticsearch【问题记录 01】启动服务&停止服务的2类方法【及 java.nio.file.AccessDeniedException: xx/pid 问题解决】(含shell脚本文件)
【4月更文挑战第12天】Elasticsearch【问题记录 01】启动服务&停止服务的2类方法【及 java.nio.file.AccessDeniedException: xx/pid 问题解决】(含shell脚本文件)
28 3
|
1天前
|
Java
Java 与垃圾回收有关的方法
Java 与垃圾回收有关的方法
|
1天前
|
存储 Java 测试技术
一文搞清楚Java中的方法、常量、变量、参数
在JVM的运转中,承载的是数据,而数据的一种变现形式就是“量”,量分为:**常量与变量**,我们在数学和物理学中已经接触过变量的概念了,在Java中的变量就是在程序运行过程中可以改变其值的量。
4 0
|
6天前
|
存储 Java
Java动态转发代理IP的实现方法
Java动态转发代理IP的实现方法
22 11
|
7天前
|
Java
Java接口中可以定义哪些方法?
【4月更文挑战第13天】
14 0
Java接口中可以定义哪些方法?
|
8天前
|
存储 安全 Java
Java中的容器,线程安全和线程不安全
Java中的容器,线程安全和线程不安全
15 1
|
10天前
|
SQL 安全 Java
Java安全编程:防范网络攻击与漏洞
【4月更文挑战第15天】本文强调了Java安全编程的重要性,包括提高系统安全性、降低维护成本和提升用户体验。针对网络攻击和漏洞,提出了防范措施:使用PreparedStatement防SQL注入,过滤和转义用户输入抵御XSS攻击,添加令牌对抗CSRF,限制文件上传类型和大小以防止恶意文件,避免原生序列化并确保数据完整性。及时更新和修复漏洞是关键。程序员应遵循安全编程规范,保障系统安全。
|
13天前
|
Java Shell
Java 21颠覆传统:未命名类与实例Main方法的编码变革
Java 21颠覆传统:未命名类与实例Main方法的编码变革
13 0
|
15天前
|
Java
Java中关于ConditionObject的signal()方法的分析
Java中关于ConditionObject的signal()方法的分析
21 4
|
1天前
|
缓存 Java
【Java基础】简说多线程(上)
【Java基础】简说多线程(上)
5 0