FlawOpen

flawopen.com/Command Injection/exec() vs execFile()

exec() frente a execFile() en Node.js: ¿qué diferencia hay?

Guía de referencia
Respuesta breve

exec() recibe una cadena y la ejecuta mediante un shell: funcionan las tuberías y los comodines, pero los metacaracteres de una entrada no confiable también pueden cambiar el comando. execFile() inicia directamente el programa indicado con una lista de argumentos y, de forma predeterminada, sin shell. Así evita el análisis del shell, pero no todos los riesgos: el programa puede interpretar un argumento como opción o actuar sobre un valor inesperado.

Comparación

exec() — usa un shell
exec(`ls ${dir} | grep .txt`,
  (err, out) => {...})
// la tubería funciona, pero también los metacaracteres.
execFile() — sin shell por defecto
execFile('ls', [dir],
  (err, out) => {...})
// sin shell; valide cómo ls interpreta el argumento dir.
// break out of its own argument

Cuándo realmente necesita funciones del shell

Si un comando requiere tuberías, expansión de comodines o redirección, execFile() no ofrece esas funciones directamente. Es preferible montar la tubería en el propio Node.js, iniciar cada etapa por separado y conectar los streams. Si exec() es inevitable, mantenga las entradas no confiables fuera de la cadena del comando. Recuerde que una lista de argumentos sin shell tampoco impide la inyección de argumentos: valide cada valor según el programa de destino, use -- cuando dicho programa lo admita y permita solo comandos y opciones previstos. La opción shell: true habilita un shell en APIs que normalmente no lo usan; en ese modo, nunca pase entradas sin sanear.

Preguntas frecuentes

Los archivos batch de Windows cambian el límite de seguridad

En Windows, los archivos .bat y .cmd no se pueden iniciar directamente con execFile(). Node documenta que pueden invocarse mediante exec(), cmd.exe o spawn() con shell habilitado; esas rutas vuelven a introducir el análisis del intérprete de comandos. Prefiere un ejecutable nativo o una API directa. Si es inevitable invocar un archivo batch, fija la ruta del script y los argumentos, o valídalos según el contrato exacto del script. Node también marca como obsoleto pasar un array de argumentos con shell: true (desde v22.15.0 y v23.11.0).

¿spawn() es igual que execFile()?

spawn() también funciona sin shell por defecto y acepta una lista de argumentos. La diferencia práctica principal es la salida: spawn() transmite streams, mientras que execFile() almacena la salida en búfer. Ninguna impide que el programa interprete un argumento como opción; valídelo para ese programa. Definir shell: true vuelve a habilitar el análisis del shell.

Referencias

Véase también Inyección de comandos del SO (CWE-78): cómo funciona y cómo evitarla en todos los lenguajes — flawopen.com Java Runtime.exec() y Command Injection: qué cambia Riesgo de command injection en Node.js exec()