Injection Attacks: SQL, Command, and LDAPLesson 2.3
What is OS command injection and how to avoid shell calls
command injection mechanics, child_process risks, shell metacharacters, execFile vs exec, avoiding shell, input allowlisting, shellescape
OS Command Injection
Command injection happens when your application passes user-controlled data to a shell. It is often more severe than SQL injection because it gives the attacker direct access to the operating system.
The Vulnerable Pattern
const { exec } = require('child_process');
// VULNERABLE — exec passes the string to /bin/sh
const filename = req.query.filename; // attacker sends: report.pdf; rm -rf /
exec(`convert ${filename} output.png`, (err, stdout) => { ... });
// Shell interprets: convert report.pdf; rm -rf / output.png
// Two commands executeFix 1: Use execFile with an Arguments Array
const { execFile } = require('child_process');
// execFile does NOT invoke a shell — arguments are passed directly
execFile('convert', [filename, 'output.png'], (err, stdout) => {
if (err) return res.status(500).json({ error: 'Conversion failed' });
res.sendFile('output.png');
});Fix 2: Allowlist Valid Input
// Only allow alphanumeric filenames with a whitelist regex
const safe = /^[a-zA-Z0-9_-]+\.(pdf|png|jpg)$/.test(filename);
if (!safe) return res.status(400).json({ error: 'Invalid filename' });
The best defense is to avoid shell calls entirely. Most tasks that seem to require a shell (file conversion, image processing, archiving) have Node.js libraries that do the same job without spawning a shell. Use sharp instead of ImageMagick via shell, archiver instead of zip via exec.
