I'm executing the following in a PHP application:
$source = '/home/user/file.ext';
$output_dir = $this->setOutputString();
chdir('/home/ben/xc/phplib/bgwatcher-2011a/a01/');
exec('php bin/createjob.php $source $output_dir', $output);
return $output[0];
The problem is this: I have control over $source, but not $output_dir, which is a legacy Windows filesystem, and there are spaces in the path. An example $output_dir is:
/home/vol1/district id/store id/this_is_the_file.html
When inserting the output string into the exec() function, I have tried both:
addslashes($output_dir) and '"' . $output_dir . '"' to escape the entire output string. In the first case, the path gets concatenated to:
/home/vol1/districtthis_is_the_file.html
... where everything between the first space and the filename gets dropped. In the second case, exec() appears to throw a shoe and doesn't execute properly - unfortunately, the error message is getting lost in the machinery - I can provide it if it's absolutely necessary, but I'm also under time constraints to find a solution.
What's the solution, here? Do I sprintf() the entire string for exec()? I'm very confused as to why addslashes isn't working correctly to escape the spaces, and I assume it has something to do with sanitization with exec(), but I can't find any documentation to back it up.
Update: I've tried escapeshellarg() and preg_replace() without success. Thinking about this further, do I need to double-escape the path? Or escape the path and the command? If the path is being unescaped once by exec(), and once by PHP before it executes the command, does it stand to reason that I need to account for both escapes? Or is that not how it works?
I don't believe addslashes() does anything with spaces. escapeshellarg() might be what you want instead. Docs on escapeshellarg
From the PHP doc (here),
Returns a string with backslashes before characters that need to be quoted in database queries etc. These characters are single quote ('), double quote ("), backslash () and NUL (the NULL byte).
This won't do anything to the spaces. What you will need to do is use str_replace() to add slashes, like this:
$new_string = str_replace(" ", "\\ ", $old_string);
According to the PHP docs,
Returns a string with backslashes before characters that need to be quoted in database queries etc. These characters are single quote ('), double quote ("), backslash () and NUL (the NULL byte).
Looks like you'll have to preg_replace the spaces yourself.
Edit:
Even though this is the topic of another discussion, if performance is an issue, then after looking into it a little more, it seems that str_replace is actually quite a bit faster than preg_replace:
The test labeled "str_replace()" was
the faster by 0.9053 seconds (it took
10.3% the time.)
The first test took 1.0093 seconds. (preg_replace)
The second test took 0.104 seconds. (str_replace)
Benchmark found here.
I've used exec() with paths with spaces before, on both Windows and Linux hosts, and in both cases quoting the path worked perfectly for me.
That said, if you have no control over the safety of a shell argument, always run it through escapeshellarg() first!
You can very well use shell quotes, since that is what all exec commands run through:
exec("php bin/createjob.php '$source' '$output_dir'", $output);
It btw works not just for arguments, but also for the command itself:
exec('"/bin/echo" "one parameter"');
Use escapeshellcmd() anyway.
this works for me when using exec() with soffice(LibreOffice):
$file_name = "Some, file name.xlsx";
exec('/usr/bin/soffice --headless --convert-to pdf '."'".$file_name."'".' 2>&1', $output, $r);
You can use double quotes and escape character together to work out this.
$fileName = "filename with spaces.pdf";
exec("php bin/createjob.php >\"".$fileName."\" 2> error.log" , $output, $return);
Related
tl;dr How do I write strpos($haystack, '^[[H^[[2J') in PHP?
A linux command line app delivers a screen-full of data followed by a console escape code to return the cursor to the "home" position in a regular loop. To access the data I piped the output to my own script like so:
$ ./otherapp | php myscript.php
It's a continuous stream of data so I use this example for non-blocking stream consumption.
Now to decode the output I need to grab a full frame/screen full of data. The escape codes are shown in nano as ^[[H and ^[[2J. The easiest way seems to be detecting these escape codes and using the output between them.
How are the escape codes represented in a PHP string? Can I use strpos (or mbstring equivalent) to detect their position?
It seems you're on a Unix environment. To achieve what your escape codes do, you can try with echo -en "\033[H" and echo -en "\033[2J", so I guess you should try something like strpos('\033[H\033[2J', $haystack).
Maybe you'll need to add an extra backslash ('\\\033[H\\\033{2J').
Since \033 means "the byte with octal value 33", which is ESC, this should work:
strpos("\e[H\e[2J", $haystack)
You need to enclose it in double-quotes (") if you want PHP to interpret escape sequences for special characters.
I have a fairly simple problem I just cannot seem to find a solution. I want to delete a line containing a certain string in all the files in a directory on my website. This string contains double quotes though. I have been trying to find a solution using backslashing but its does not work.
The string I want to replace is:session_register("c$key");
I'm using this php and unix code:
$command = "sed -i '/session_register(\"c$key\")/d;' ./*";
$output = shell_exec($command);
I want to delete this line because session_register is outdated.
It does not do anything but does not give me an error. What is the problem or how should I backslash?
Thanks a lot!
You need to double escape $ as well since $ is special regex symbol means end of input. Also use single quote instead of double for your $command string assignment.
Try this:
$command = 'sed -i.bak \'/session_register("c\\$key")/d;\' ./*';
$output = shell_exec($command);
I'm trying to create a PHP script that creates a file on a remote linux server through ssh, and echos the file contents into it.
However, I cannot figure out how to correctly and safely encode/escape the file contents, so that the contents don't get interpreted as commands.
I'm using phpseclib from here.
I've tried something like
echo $ssh->exec('sudo echo "' . escapeshellarg($newConfig) . '" > /etc/nginx/nginx.conf') . "\n";
but without success.
Thanks,
Steve
What about escapeshellcmd? Quoting the description of that vs escapeshellarg:
escapeshellarg() adds single quotes around a string and quotes/escapes
any existing single quotes allowing you to pass a string directly to a
shell function and having it be treated as a single safe argument.
...and...
escapeshellcmd() escapes any characters in a string that might be used
to trick a shell command into executing arbitrary commands. This
function should be used to make sure that any data coming from user
input is escaped before this data is passed to the exec() or system()
functions, or to the backtick operator.
Following characters are preceded by a backslash: #&;`|*?~<>^()[]{}$\,
\x0A and \xFF. ' and " are escaped only if they are not paired. In
Windows, all these characters plus % are replaced by a space instead.
I was going about this all wrong, I should have used Net_SFTP instead of NET_SSH for this sort of thing.
I am writing php code which will work under *nix systems. I need to call a python script with 5 arguments inside php code. Some of arguments
are user input so there could be any characters. I cannot figure out how pass arguments to a script. How would I separate arguments?. For example in this question you separate by end of line character, but in my arguments could be anything written by users. How to pass such arguments? any ideas?
EDIT:
I have idea of putting escape character in each quotes symbols ' or " before
passing arguments, on the other end I will get rid of escape character. Does json encoding do it?
The function you want is escapeshellcmd():
$arg1 = escapeshellarg($input1);
$arg2 = "foo";
$arg3 = escapeshellarg($input3);
$arg4 = "bar";
$arg5 = escapeshellarg("a string containing spaces and other *special* characters");
$proc = proc_open("python_prog $arg1 $arg2 $arg3 $arg4 $arg5", ...);
In the other thread, the program didn't take any arguments, the newlines were being used to separate items on standard input.
You're basically executing a command on the command prompt; you might want to familiarize yourself with a command prompt first.
Parameters are separated by spaces. So if your inputs have spaces in them, you have to put quotes around these inputs (I suggest single quotes; using a double quote will lead to environment variables being expanded, among other things).
So basically, you have to escape all the single quotes, line breaks, and carriage returns in your inputs, surround each of them with single quotes and append them to the command.
Warning: Security-wise, this whole thing is very problematic. If your escape mechanism is not bullet-proof, anyone would be able to execute a command on your server.
An alternate answer would be to write the inputs into a temporary file, and read from this file in your python script. If you have control over this script, I would strongly suggest you to do that.
I've been having trouble running a command using PHP's exec() function on Windows. Per a comment on PHP's site on exec():
In Windows, exec() issues an internal call to "cmd /c your_command".
My command looks like:
"path\to\program.exe" -flag1 attribute1 -flag2 attribute2 -flag3 "attribute3 attribute4"
Under regular execution of this command in my local command prompt, without the /c flag, the command runs fine. However, with the introduction of the /c flag, command prompt tells me that "The system cannot find the path specified."
I think the command prompt is interpreting the double-quoted argument as a path to another file, but that's the furthest I've gotten with this problem.
Does anybody have any ideas on how to get past this? Thanks!
I also encountered this issue, and the cause of it is, indeed, the internal use of "cmd /c" as described in your own answer.
I have done some investigation, and have found that this was resolved in PHP 5.3, which is why some commenters were unable to reproduce it.
It was fixed in the following commit:
https://github.com/php/php-src/commit/19322fc782af429938da8d3b421c0807cf1b848a#diff-15f2d3ef68f383a87cce668765721041R221
For anyone who still needs to support PHP 5.2, it is fairly easy to replicate the fix in your own code. Instead of:
$cmd = "...any shell command, maybe with multiple quotes...";
exec($cmd);
use
function safe_exec($cmd, &$output = null, &$result_code = null) {
if (strtoupper(substr(php_uname('s'), 0, 3)) == "WIN"
&& version_compare(PHP_VERSION, "5.3", "<"))
{
$cmd = '"' . $cmd . '"';
}
return exec($cmd, $output, $result_code);
}
$cmd = "...any shell command, maybe with multiple quotes...";
safe_exec($cmd);
This replicates the behaviour of PHP 5.3 and above, in the same way as in the above-linked commit.
Note that this applies to all shell commands, not just exec() as used in my example.
I've figured out the answer by myself...
After perusing cmd.exe's /? and trying to decipher that, I've noticed one important section:
If all of the following conditions are met, then quote characters on the command line are preserved:
No /S switch (Strip quotes)
Exactly two quote characters
No special characters between the two quote characters, where special is one of: & < >( ) # ^ |
There are one or more whitespace characters between the the two quote characters
The string between the two quote characters is the name of an executable file.
Otherwise, old behavior is to see if the first character is a quote character and if so, strip the leading character and remove the last quote character on the command line, preserving any text after the last quote character. To negate this behaviour use a double set of quotes "" at the start and end of the command line.
It seems as though if there more than one pair of quotes at any time, quotation marks will be stripped from the second pair of quotes and on.
A relevant question: How do I deal with quote characters when using cmd.exe but not completely relevant, since PHP will not allow you to modify its exec() command by putting an /S flag on its call (which would definitely be a lot easier).
I've managed to work around this problem by directly changing directories with chdir() to the folder where the .exe is located, then chdir()'ing back to my original working directory. It's an extremely hacky solution, given the case that I'm lucky that I only have one set of arguments using double quotes. If anybody were to have multiple arguments using double quotes, I wouldn't know how to solve that...
Just a guess (I am not familiar with PHP on windows): maybe escape the quotes as " becoming ""?
"path\to\program.exe" -flag1 attribute1 -flag2 attribute2 -flag3 ""attribute3 attribute4""
Whatever the solution is, make sure that when there's some form of user-input that gets passed to this command as arguments that you use escapeshellarg and/or escapeshellcmd.
I hope it will help
escapeshellarg() — Escape a string to be used as a shell argument
escapeshellcmd() — Escape shell metacharacters