java下exec命令执行问题
字数 981 2025-08-11 17:40:17

Java中Runtime.exec()命令执行安全指南

1. Runtime.exec()基本用法

Java中执行系统命令主要通过Runtime.getRuntime().exec()方法,有两种基本形式:

1.1 单字符串形式

Runtime.getRuntime().exec("command");
  • 只传递一个字符串参数
  • 字符串代表一个没有参数的纯命令,如hostnamepwd
  • 不能包含命令参数或连接符

1.2 字符串数组形式

String[] cmds = {"/bin/sh", "/root/tmp/xxx.sh", "xxx"};
Runtime.getRuntime().exec(cmds);
  • 第一个元素是命令本身
  • 后续元素都是命令的参数
  • 参数会被当作普通字符串处理,不会解析特殊字符

2. 执行复杂命令的方法

2.1 通过shell执行复杂命令

String[] cmds = {"/bin/sh", "-c", "ps -ef | grep xxx | grep -v grep | awk '{ print $2 }'"};
Runtime.getRuntime().exec(cmds);
  • 使用/bin/sh -c可以将多操作命令合并成一个完整命令执行
  • -c选项使shell从字符串中读取命令

2.2 执行带参数的命令

String[] cmds = {"ffmpeg", "-i", "input.mp4", "-hide_banner"};
Runtime.getRuntime().exec(cmds);
  • 每个参数作为数组的独立元素
  • 参数中的特殊字符不会被shell解析

3. 命令注入安全问题

3.1 常见错误用法

// 危险示例:用户输入直接拼接
String url = "www.baidu.com&ipconfig"; // 用户可控输入
Runtime.getRuntime().exec("ping " + url);
  • 在命令行终端中,ping www.baidu.com&ipconfig会执行两条命令
  • 但在Java中,整个字符串会被当作ping命令的一个参数,不会执行命令注入

3.2 安全限制

  • 字符串数组形式中,从第二个元素开始都被当作参数,不会解析为命令
  • 连接符(&, |, ;等)在参数中不会被shell解析
  • 只有控制命令字符串的第一个元素才可能实现命令注入

3.3 安全编码建议

  1. 避免直接拼接用户输入到命令中
  2. 使用字符串数组形式而非单字符串形式
  3. 对用户输入进行严格过滤和转义
  4. 使用白名单验证命令参数

4. 实际案例分析

4.1 安全示例

ArrayList<String> sites = new ArrayList<>();
sites.add("ffmpeg-amd64-3.0.0.exe");
sites.add("-i");
sites.add("D:\\test.txt | mkdir E:\\xixihaha111222222");
sites.add("-hide_banner");
Runtime.getRuntime().exec((String[])sites.toArray(new String[0]));
  • 即使第三个参数包含| mkdir命令,也不会被执行
  • 整个字符串D:\\test.txt | mkdir E:\\xixihaha111222222被当作普通参数

4.2 潜在风险场景

String[] cmds = {userControlledInput, "arg1", "arg2"};
Runtime.getRuntime().exec(cmds);
  • 如果用户能控制第一个元素(命令本身),则可能实现命令注入
  • 解决方案:固定命令路径,只允许用户控制参数

5. 总结

  1. Java的Runtime.exec()对命令注入有一定防护:

    • 字符串数组形式中参数不会被解析为命令
    • 连接符在参数中无效
  2. 安全使用要点:

    • 优先使用字符串数组形式
    • 避免用户控制命令本身
    • 对用户输入进行严格验证
  3. 完全安全的做法:

    • 使用ProcessBuilder替代Runtime.exec()
    • 实现完整的输入验证机制
    • 遵循最小权限原则执行命令
Java中Runtime.exec()命令执行安全指南 1. Runtime.exec()基本用法 Java中执行系统命令主要通过 Runtime.getRuntime().exec() 方法,有两种基本形式: 1.1 单字符串形式 只传递一个字符串参数 字符串代表一个没有参数的纯命令,如 hostname 、 pwd 等 不能包含命令参数或连接符 1.2 字符串数组形式 第一个元素是命令本身 后续元素都是命令的参数 参数会被当作普通字符串处理,不会解析特殊字符 2. 执行复杂命令的方法 2.1 通过shell执行复杂命令 使用 /bin/sh -c 可以将多操作命令合并成一个完整命令执行 -c 选项使shell从字符串中读取命令 2.2 执行带参数的命令 每个参数作为数组的独立元素 参数中的特殊字符不会被shell解析 3. 命令注入安全问题 3.1 常见错误用法 在命令行终端中, ping www.baidu.com&ipconfig 会执行两条命令 但在Java中,整个字符串会被当作ping命令的一个参数,不会执行命令注入 3.2 安全限制 字符串数组形式中,从第二个元素开始都被当作参数,不会解析为命令 连接符(&, |, ;等)在参数中不会被shell解析 只有控制命令字符串的第一个元素才可能实现命令注入 3.3 安全编码建议 避免直接拼接用户输入到命令中 使用字符串数组形式而非单字符串形式 对用户输入进行严格过滤和转义 使用白名单验证命令参数 4. 实际案例分析 4.1 安全示例 即使第三个参数包含 | mkdir 命令,也不会被执行 整个字符串 D:\\test.txt | mkdir E:\\xixihaha111222222 被当作普通参数 4.2 潜在风险场景 如果用户能控制第一个元素(命令本身),则可能实现命令注入 解决方案:固定命令路径,只允许用户控制参数 5. 总结 Java的 Runtime.exec() 对命令注入有一定防护: 字符串数组形式中参数不会被解析为命令 连接符在参数中无效 安全使用要点: 优先使用字符串数组形式 避免用户控制命令本身 对用户输入进行严格验证 完全安全的做法: 使用ProcessBuilder替代Runtime.exec() 实现完整的输入验证机制 遵循最小权限原则执行命令