In Node, I'm using a module (GM) and noticed that it uses spawn
from the child_process
module to pass arguments to GraphicMagick's convert
executable.
I'm passing user-submitted information to GM. Is there a security concern that the user could do some sort of injection attack using a pipe (or other command line trickery)? Or does spawn
protect against that? If not, is there a best practice for escaping user submitted values in this case?
spawn() or child_process. spawnSync() . child_process. exec() : spawns a shell and runs a command within that shell, passing the stdout and stderr to a callback function when complete.
spawn() method: This method spawns a new process using the given command and the command line arguments in args. The ChildProcess instance implements EventEmitterAPI which enables us to register handlers for events on child object directly.
While node streams are async generators, this will return after the first chunk and break as soon as the stream receives multiple chunks. So you either should replace return by yield and return an async generator, or concatenate the buffers with the loop and return after the loop completes.
We recently published a blog post on avoiding command injection vulnerabilities in node.js. It explains a bit about how spawn prevents this.
If gm was using child_process.exec there would be a greater chance of injection. This is because child_process.exec executes the commands under a subshell and not directly, letting shell meta characters like backticks, $(), ;, &&, || etc to be used nefariously.
The resulting system call looks like this with .exec() for a simple ls -l that might take user input.
[pid 25170] execve("/bin/sh", ["/bin/sh", "-c", "ls -l user input"], [/* 16 vars */]
Since gm uses spawn the resulting system call would look something like this.
[pid 25565] execve("/bin/ls", ["/bin/ls", "-l", "."], [/* 16 vars */]
As gm would be the first argument to execve. This means that a user cannot run subcommands in the shell using pipes and other command line trickery, because in our example /bin/ls has no idea what to do with backticks or pipes or ;. It’s /bin/bash that is going to be interpreting those commands. It’s similar to using parametrized vs string-based SQL queries, if you are familiar with that.
This does however come with a caveat: using spawn is not always a safe thing. User provided arguments could still potentially have a bad outcome, maybe not command injection but something else. Check with the behavior of gm and the arguments that you are pass in user provided input into and think about how the user might be able to abuse that argument.
So, here’s the generic collective guidance for running system commands from node.js:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With