C++: How to escape user input for safe system calls?
On a Linux platform, I have C++ code that goes like this:
// ...
std::string myDir;
myDir = argv[1]; // myDir is initialized using user input from the command line.
std::string command;
command = "mkdir " + myDir;
if (system(command.c_str开发者_运维技巧()) != 0) {
return 1;
}
// continue....
- Is passing user input to a system() call safe at all?
- Should the user input be escaped / sanitized?
- How?
- How could the above code be exploited for malicious purposes?
Thanks.
Just don't use system
. Prefer execl
.
execl ("/bin/mkdir", "mkdir", myDir, (char *)0);
That way, myDir
is always passed as a single argument to mkdir
, and the shell isn't involved. Note that you need to fork
if you use this method.
But if this is not just an example, you should use the mkdir
C function:
mkdir(myDir, someMode);
Using system() call with command line parameters without sanitizing the input can be highly insecure.
The potential security threat could be a user passing the following as directory name
somedir ; rm -rf /
To prevent this , use a mixture of the following
- use getopt to ensure your input is sanitized
- sanitize the input
- use execl instead of system to execute the command
The best option would be to use all three
Further to Matthew's answer, don't spawn a shell process unless you absolutely need it. If you use a fork/execl combination, individual parameters will never be parsed so don't need to be escaped. Beware of null characters however which will still prematurely terminate the parameter (this is not a security problem in some cases).
I assume mkdir is just an example, as mkdir can trivially be called from C++ much more easily than these subprocess suggestions.
Reviving this ancient question as I ran into the same problem and the top answers, based on fork() + execl(), weren't working for me. (They create a separate process, whereas I wanted to use async to launch the command in a thread and have the system call stay in-process to share state more easily.) So I'll give an alternative solution.
It's not usually safe to pass user input as-is, especially if the utility is designed to be sudo'd; in order to sanitize it, instead of composing the string to be executed yourself, use environment variables, which the shell has built-in escape mechanisms for.
For your example:
// ...
std::string myDir;
myDir = argv[1]; // myDir is initialized using user input from the command line.
setenv("MY_DIR", myDir, 1);
if (system("mkdir \"${MY_DIR}\"") != 0) {
return 1;
}
// continue....
精彩评论