flawopen.com/Command Injection/exec() и execFile()
В чём разница между exec() и execFile() в Node.js?
exec() принимает строку и выполняет её через shell: конвейеры и шаблоны работают, но метасимволы в недоверенном вводе могут изменить команду. execFile() напрямую запускает указанную программу с массивом аргументов и по умолчанию без shell. Это исключает разбор shell, но не все риски: целевая программа всё ещё может трактовать аргумент как опцию или выполнять действие с неожиданным значением.
Сравнение
exec(`ls ${dir} | grep .txt`,
(err, out) => {...})
// конвейер работает, но метасимволы тоже.
execFile('ls', [dir],
(err, out) => {...})
// shell не запускается; проверьте, как ls трактует аргумент dir.
// break out of its own argument
Когда действительно нужны функции shell
Если команде необходимы конвейеры, раскрытие шаблонов или перенаправление, execFile() не предоставляет эти возможности напрямую. Предпочтительно собрать конвейер в Node.js: запускать каждый этап отдельно и соединять потоки. Если без exec() не обойтись, не включайте недоверенный ввод в строку команды. Массив аргументов без shell также не защищает от инъекции аргументов: проверяйте каждое значение с учётом целевой программы, используйте --, если программа это поддерживает, и разрешайте только ожидаемые команды и опции. Параметр shell: true включает shell в API, которые обычно его не запускают; в этом режиме нельзя передавать непроверенный ввод.
Частые вопросы
Batch-файлы Windows меняют границу безопасности
В Windows файлы .bat и .cmd нельзя запустить напрямую через execFile(). В документации Node указано, что их можно вызывать через exec(), cmd.exe или spawn() с включённой оболочкой; при этом снова появляется разбор командным интерпретатором. Предпочитайте нативный исполняемый файл или прямой API. Если вызов batch-файла неизбежен, зафиксируйте путь к скрипту и аргументы либо проверяйте их по точному контракту скрипта. Node также объявил устаревшей передачу массива аргументов при shell: true (начиная с v22.15.0 и v23.11.0).
spawn() также по умолчанию работает без shell и принимает массив аргументов. Главное практическое различие — обработка вывода: spawn() передаёт потоки, а execFile() буферизует вывод. Ни один API не мешает программе трактовать аргумент как опцию — проверяйте его с учётом этой программы. Значение shell: true снова включает разбор shell.