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");
- 只传递一个字符串参数
- 字符串代表一个没有参数的纯命令,如
hostname、pwd等 - 不能包含命令参数或连接符
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 安全编码建议
- 避免直接拼接用户输入到命令中
- 使用字符串数组形式而非单字符串形式
- 对用户输入进行严格过滤和转义
- 使用白名单验证命令参数
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. 总结
-
Java的
Runtime.exec()对命令注入有一定防护:- 字符串数组形式中参数不会被解析为命令
- 连接符在参数中无效
-
安全使用要点:
- 优先使用字符串数组形式
- 避免用户控制命令本身
- 对用户输入进行严格验证
-
完全安全的做法:
- 使用ProcessBuilder替代Runtime.exec()
- 实现完整的输入验证机制
- 遵循最小权限原则执行命令