flawopen.com/Command Injection/exec() vs execFile()
exec() frente a execFile() en Node.js: ¿qué diferencia hay?
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(`ls ${dir} | grep .txt`,
(err, out) => {...})
// la tubería funciona, pero también los metacaracteres.
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() 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.