05 / 05

What risks does using subprocess with shell=True and untrusted input introduce?

Difficulty: 8/10
Subprocess, Shell Injection, Command Injection, Argument Injection

shell=True passes input to the shell, enabling command injection via metacharacters

When shell=True is used, subprocess hands the command string to /bin/sh (or cmd.exe on Windows). The shell interprets metacharacters such as ;, |, &&, $(), and backticks. If any part of the command comes from user input, an attacker can break out of the intended command and execute arbitrary commands with the privileges of the Python process. Even without shell=True, argument injection is possible if user input starts with a dash and is interpreted as a flag by the target program. The safe pattern is shell=False with a list of arguments, which passes each argument directly to execve without shell parsing. If you must use a shell, quote each argument with shlex.quote and validate against an allowlist of allowed characters or values. Never interpolate untrusted input into a shell command. Also avoid shell=True when the command is static, because it adds a shell process and platform-specific behavior for no benefit.

  1. 1

    shell=False with a list: the program receives arguments exactly as given, with no shell interpretation.

  2. 2

    shell=True: the string is parsed by the shell, so ;, |, &&, $(), and backticks are dangerous.

  3. 3

    Argument injection: even with shell=False, a value starting with '-' can be treated as an option by the target program.

  4. 4

    shlex.quote can be used when shell=True is unavoidable, but allowlisting is safer.

  5. 5

    Common mistake: using shell=True with an f-string that includes user input. This is command injection.

  6. 6

    Common mistake: assuming that escaping spaces is enough. Shell metacharacters are the real risk.

  7. 7

    Version note: subprocess.run is the recommended modern API since 3.5. Older call, check_output, and Popen still work but are not preferred for new code.

Scenario Questions

0-2 years experience

  1. 1Why is subprocess.run('ls ' + user_input, shell=True) dangerous?
  2. 2How do you run a command with arguments safely in Python?

2-5 years experience

  1. 1You need to run ffmpeg with a user-supplied filename. How do you prevent injection?
  2. 2A script uses shell=True with a static command. Is it safe? What are the downsides?

5-8 years experience

  1. 1You must integrate with a CLI tool that requires shell features like pipes. How do you do it safely?
  2. 2You discover shell=True with user input in a production service. How do you assess the blast radius and remediate?

8+ years experience

  1. 1Design a safe command execution layer that supports plugins, sandboxing, and auditing without exposing shell injection.
  2. 2Explain how to test for command injection vulnerabilities in a Python codebase, including static analysis and fuzzing.

Follow-up Questions

  • How does shlex.quote mitigate shell injection, and when is it insufficient?
  • What is argument injection and how does it differ from command injection?
Share

Share via WhatsApp, X, Facebook, LinkedIn or copy link. Open Graph preview enabled.