FlawOpen

flawopen.com/Command Injection/Java Runtime.exec()

Защищает ли Java Runtime.exec() от Command Injection?

CWE-78: OS Command InjectionСправочная страница
Краткий ответ

Runtime.exec(String) не запускает shell автоматически, поэтому операторы shell не интерпретируются им как команды. Но строка всё равно разбивается на программу и аргументы, и недоверенный текст может изменить аргументы целевой программы. ProcessBuilder явно задаёт границы аргументов; при этом необходимо проверять каждое значение и ограничить исполняемый файл и допустимые параметры.

РИСКОВАННО
// Пользовательский ввод добавляется к строке команды
Runtime.getRuntime().exec(
  "approved-tool --input " + filename
);
БЕЗОПАСНЕЕ
// Фиксированная программа и отдельные аргументы; сначала проверьте filename
String safeName = validateFilename(filename);
new ProcessBuilder(
  "/opt/app/bin/approved-tool",
  "--input", safeName
).start();

Инъекция в shell и инъекция аргументов — разные угрозы

Начиная с Java 18, Oracle помечает Runtime.exec(String) как устаревший из-за склонности к ошибкам и рекомендует exec(String[]) или ProcessBuilder. Строковая перегрузка делит команду по пробельным символам, поэтому, например, путь с пробелами может распасться на части. Runtime.exec(String) не запускает автоматически sh или другую оболочку; Oracle указывает, что строка преобразуется в массив аргументов. Поэтому обычные метасимволы shell не становятся синтаксисом оболочки, но ввод всё ещё опасен: токенизация может создать дополнительные аргументы, а целевая программа способна трактовать аргумент как опцию. ProcessBuilder принимает список строк с программой и аргументами; на распространённых платформах каждый элемент задаёт границу аргумента. Зафиксируйте исполняемый файл, проверяйте значения по ожидаемому формату программы и отклоняйте или безопасно обрабатывайте значения, похожие на опции. Используйте -- только если документация программы гарантирует такое поведение. Не запускайте оболочку явно, например через sh -c, с недоверенными данными.

Частые вопросы

Безопасен ли Runtime.exec(String), раз он не запускает shell?

Нет. Это снижает риск инъекции в shell для данной перегрузки, но инъекция аргументов остаётся возможной. Программа может принять ввод атакующего за опцию или имя файла. Используйте ProcessBuilder с фиксированной программой и отдельными аргументами и проверяйте каждое значение с учётом целевой программы.

Проверяет ли ProcessBuilder аргументы за меня?

Нет. Он формирует список программы и аргументов и запускает процесс, но не проверяет их безопасность и допустимость. Добавьте списки разрешённых значений и проверку в приложении и соблюдайте документированные правила аргументов программы.

Источники

См. также Внедрение команд ОС (CWE-78): как это работает и как предотвратить на любом языке — flawopen.com Риск command injection в Node.js exec() Node.js exec() и execFile(): различия и безопасность