Course contentsShow
← Node.js
Lesson 1785 of 1,98016. Security Hardening and Threat MitigationFree lesson

Defending Against Command Injection Patterns

Sanitize user-provided arguments in child_process executions to prevent arbitrary shell access.

In this lesson, you will learn how to neutralize command injection risks by isolating user-provided input from shell execution logic within Node.js.

The Shell Injection Problem

When you use child_process.exec(), Node.js spawns a shell (like /bin/sh or cmd.exe) to execute the string you provide. If an attacker can inject malicious characters (e.g., ;, &, or |) into your input, they can break out of your intended command and execute arbitrary code with the privileges of your Node.js process.

Consider this vulnerable pattern:

const { exec } = require('child_process');

// DANGEROUS: User provides 'file.txt; rm -rf /'
exec(`ls -l ${userInput}`, (err, stdout) => {
  console.log(stdout);
});

Here, the shell parses the semicolon and executes the rm command, treating your input as part of the command pipeline.

The Defensive Solution: Argument Array Separation

To prevent this, you must avoid shell interpolation entirely. The child_process.execFile() and child_process.spawn() methods accept commands and arguments as a separate array. Because these methods bypass the shell, the operating system treats the entire string as a literal argument rather than as part of an executable command string.

Here is the hardened approach:

const { execFile } = require('child_process');

// SAFE: The input is passed as a literal string argument to 'ls'
execFile('ls', ['-l', userInput], (err, stdout) => {
  if (err) return console.error(err);
  console.log(stdout);
});

By providing the arguments as an array, the input ; rm -rf / is passed safely to ls. The ls utility will simply report that a file named ; rm -rf / does not exist, effectively neutralizing the attack.

Always treat user input as data, never as executable code. When your application requires interactions with the system shell, explicitly favor methods that allow parameter separation to prevent context-switching within the shell's command interpreter.

Key Takeaway: Never pass unsanitized user strings to exec(); use execFile() or spawn() with argument arrays to force the system to treat input as literal data rather than executable shell syntax.