flawopen.com/Command Injection/Node child_process.exec
Risiko command injection pada Node.js child_process.exec()
Memberikan string perintah ke exec() seperti menyerahkan kalimat kepada penata huruf dan membiarkan tanda baca menentukan batas instruksi. Jika teks tak tepercaya berisi sintaks shell, shell dapat membacanya sebagai instruksi kontrol. execFile() mengirim program tetap dan argumen terpisah tanpa parsing shell secara default.
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
});
}
Mengapa execFile() mengurangi risiko command injection
exec() memulai shell dan memberikan string perintah kepadanya. Jika kode menyisipkan teks yang dikendalikan penyerang, shell memproses metakarakter sebagai sintaks: inilah jalur command injection yang dijelaskan CWE-78. execFile() menjalankan executable tertentu dengan array argumen dan secara default tidak membuat shell, sehingga pemisah shell tidak diproses ulang oleh shell. Contoh ini juga memetakan ID pemanggil ke path tetap. Ini tidak membuat semua program target aman: validasi argumen yang diterima program, gunakan terminator opsi seperti -- jika didukung, dan jangan aktifkan shell: true untuk nilai tak tepercaya.
Cara menemukan pemanggilan exec() yang berisiko
Cari exec() dan execSync(), lalu telusuri setiap string perintah ke parameter request, kolom database, nama file, dan variabel lingkungan. Catat executable dan batas tiap argumen. Utamakan API langsung; jika tidak memungkinkan, gunakan execFile() dengan executable tetap, argumen tervalidasi, batas waktu, dan batas keluaran.
grep -RInE 'exec(Sync)?[[:space:]]*\(' --include='*.js' --include='*.cjs' --include='*.mjs' --include='*.ts' .Varian sinkron
execSync() memakai string perintah berbasis shell yang sama sehingga risikonya sama, sekaligus memblokir event loop. execFileSync() secara default menghindari parsing shell, tetapi executable dan argumennya tetap harus divalidasi.