flawopen.com/Command Injection/Node child_process.exec
Risco de command injection em Node.js child_process.exec()
Passar uma string de comando a exec() é como entregar uma frase a um tipógrafo e deixar a pontuação decidir onde as instruções se dividem. Se texto não confiável contiver sintaxe do shell, o shell poderá tratá-la como instrução. execFile() envia um programa fixo e argumentos separados, sem interpretar um shell por padrão.
const { exec } = require('node:child_process');
function inspectFile(filename) {
return exec(`file ${filename}`);
}
const { execFile } = require('node:child_process');
const allowed = new Map([
['report-1', '/srv/reports/1.pdf'],
['report-2', '/srv/reports/2.pdf']
]);
function inspectReport(reportId) {
const path = allowed.get(reportId);
if (!path) throw new Error('Unknown report');
return execFile('/usr/bin/file', ['--', path], {
shell: false,
timeout: 3000,
maxBuffer: 64 * 1024
});
}
Por que execFile() reduz o risco de command injection
exec() inicia um shell e passa a ele uma string de comando. Se o código interpolar texto controlado por um atacante nessa string, o shell analisará metacaracteres como sintaxe: esse é o caminho de command injection descrito pela CWE-78. execFile() inicia um executável específico com um array de argumentos e, por padrão, não cria um shell; assim, separadores de shell não são reanalisados pelo shell. O exemplo também associa o ID recebido a um caminho fixo. Isso não torna seguro qualquer programa de destino: valide os argumentos aceitos pelo programa, use um terminador como -- quando houver suporte e nunca habilite shell: true para valores não confiáveis.
Como localizar chamadas inseguras a exec()
Busque exec() e execSync() e rastreie cada string de comando até parâmetros de requisição, campos do banco, nomes de arquivo e variáveis de ambiente. Registre o executável e os limites de cada argumento. Prefira uma API direta; caso contrário, use execFile() com executável fixo, argumentos validados, tempo de execução limitado e saída limitada.
grep -RInE 'exec(Sync)?[[:space:]]*\(' --include='*.js' --include='*.cjs' --include='*.mjs' --include='*.ts' .Variantes síncronas
execSync() usa a mesma string de comando baseada em shell e apresenta o mesmo risco de injeção, além de bloquear o event loop. execFileSync() evita a interpretação pelo shell por padrão, mas o executável e os argumentos ainda precisam ser validados.