The primary defense against OS command injection is not to call OS commands at all. Use a library function instead. If you must run a program, pass the command and each argument as separate list items rather than one string, and validate arguments against an allowlist. Escaping alone leaves argument injection open.
Start with the library
OWASP's command injection cheat sheet says the primary defense is to avoid calling OS commands directly. Language built ins and libraries cannot be talked into doing something else. Its example: call a library's directory creation function rather than running mkdir.
If you must run a process
Pass arguments as a list. In Java, that means ProcessBuilder, where the command is separated from its arguments. The shell never parses the input, so characters such as & or | are not interpreted.
import subprocess
subprocess.run(["convert", input_path, output_path], check=True)
Avoid running sh -c, cmd.exe or powershell.exe with untrusted input. OWASP names these explicitly.
Why escaping is not enough
Escaping, such as PHP's escapeshellarg(), stops shell metacharacters from starting extra commands. But OWASP points out an attacker can still pass a single argument to the command you intended. Escaping does not stop option injection into that command.
OWASP puts it strongly: every OS command injection is also an argument injection. If an attacker can add an option such as --help to a curl call, the escaped input still reaches the program as a valid argument. Impact ranges from information disclosure to remote code execution depending on the program.
Argument injection in practice
Many programs treat anything starting with a dash as an option. If you pass user text where a filename belongs, a value that looks like an option may change behaviour. OWASP notes that a double dash can end option parsing for some commands, but it does not stop extra URL operands in curl. So check what your specific program does.
Allowlists
When a command cannot be avoided, OWASP says to validate both the command and its arguments:
- The command must be in a list of allowed commands.
- Arguments should come from a positive list of permitted values, or match an allowlist regular expression with a length limit, such as a short lowercase alphanumeric pattern.
- Exclude metacharacters and whitespace from the allowed characters.
OWASP's separate injection page adds that argument separation does not replace argument validation.
Review steps
- Search for
system,exec,popen,shell=True, backticks and similar calls. - For each, ask whether a library function can replace it.
- Convert string commands to argument lists.
- Validate each argument with an allowlist.
- Run the process with the least privilege and a restricted working directory.
- Test with inputs that start with a dash and with spaces, quotes and separators.
Quick check
Is shell=True ever acceptable with user input?
Avoid it. Without the shell, metacharacters are not interpreted, which removes the largest class of mistakes.
Sources
Last reviewed: 19 October 2026.