A trap of shell=True in the subprocess module

If you look into a page of a subprocess module you find a few red boxes warning you that shell=Trueis insecure. Malicious user may use shfeatures to execute unexpected code and so on.

At the same page you will find another remark, less visual, but in my opinion, much more important:

On Unix with shell=True … If args is a string, the string specifies the command to execute through the shell … If args is a sequence, the first item specifies the command string, and any additional items will be treated as additional arguments to the shell itself.

One may even didn’t notice such a huge change at a first glance.

Let’s look on this example:

>>> subprocess.call([‘id’, ‘root’])
uid=0(root) gid=0(root) groups=0(root)
>>> subprocess.call(['id', 'root'], shell=True)
uid=1000(user) gid=1000(user) groups=1000(user),24(cdrom),25(floppy), ...

You may be amused by this. Without quotation above it’s almost impossible to figure out what had happened.

When you add shell=True, and pass command line arguments as a list, only first element of the list will be executed. Rest will be ignored or would change sh behavior if it contains valid shell arguments.

Basically, when you add shell=True it completely changes interpretation for command line:

  • When shell=False, args[:] is a command line to execute
  • When shell=True, args[0] is a command line to execute and args[1:] is arguments to sh.

(I repeated this idea twice, because I still can’t believe anyone would design such a grotesque interface for the standard library of commonly-used language…)

Show your support

Clapping shows how much you appreciated George Shuklin’s story.