Bash and PHP, quoting command line arguments - php

I have a PHP program that uses a Bash script to convert a pdf. However if the filename contains spaces it is not passed through the bash script correctly.
How do you escape filenames with spaces within a bash script? Do you have to do something special to quote the filename for the "OUTFILE" variable?
Bash script:
#!/bin/bash
INFILE=$1
OUTFILE=${INFILE%.*}
gs \
-q \
-dSAFER \
-dBATCH \
-dNOPAUSE \
-sDEVICE=png256 \
-r150x150 \
-sOutputFile=${OUTFILE}.png \
${INFILE}
PHP script:
echo "converting: ".$spool.$file . "\n";
system("/home/user/bin/pdf2png.sh " . escapeshellarg($spool . $file));
Edit: I removed the quotes around the escapeshellarg() variable. This however did not fix the problem. Which I think is in the Bash script OUTFILE variable.

In the last line of your shell script, put quotes around the variable reference:
"${INFILE}"

Considering your code, I would try by, first, removing the single-quotes you are inserting arround the parameter : those shouldn't be necessary, as you are using escapeshellarg.
For instance, the temp.php file might contain :
$spool = "ab cd/";
$file = "gh ij";
system("sh ./test.sh " . escapeshellarg($spool . $file) . "");
And the test.sh :
#!/bin/bash
INFILE=$1
echo $1
With those, the output is :
$ php temp.php
ab cd/gh ij
Which looks like what you expect.
If I put back the single-quotes, like this :
system("sh ./test.sh '" . escapeshellarg($spool . $file) . "'");
The output is broken again :
$ php temp.php
ab
escapeshellarg is escaping data for you (with the right quotes and all that, depending on the operating system), you don't have to do that yourself.

To escape a file name with spaces, just use .
My Tar Ball.tar
My\ Tar\ Ball.tar
So for what you have you will either need to make sure that your arguments contain the back slashes when the script is called, or you will need to add some input checking.

Take a look at escapeshellcmd() and escapeshellarg()

Related

Correct escaping/quotation for Perl script executed by PHP exec on Linux [duplicate]

I use the Exiv2 command line tool on Linux to edit image metadata like so:
exiv2 -M"set Iptc.Application2.Caption String This is my caption....." modify IMG.jpg
I want to execute this from PHP, using a caption provide by a user. This will work if the user enters no special characters:
exec('/usr/local/bin/exiv2 -M"set Iptc.Application2.Caption String '.$caption.'" modify IMG.jpg');
I need to allow the user special characters such as single and double quotes. I would like to use escapeshellcmd() to prevent malicious data. How can I correctly escape the command and the argument so that it works? I have tried many options but I can't get it right.
Yes, this is a hard problem, because the command is using non-standard shell arguments (like it's own little meta-language). ImageMagick has the same issues.
If you simply use escapeshellarg() within a double-quoted string, it is rendered useless. escapeshellcmd() does escape all special characters, and is safe for use in a double-quoted string. So you need to hard code single quotes around it to make it work right.
exec('/usr/local/bin/exiv2 -M"set Iptc.Application2.Caption String \'' . escapeshellcmd($caption) . '\'" modify IMG.jpg');
The reason escapeshellarg() will not work in a single quoted string is:
# for this input:
The smith's "; rm -rf *; echo "went to town
# after escapeshellarg()
'The smith\'s "; rm -rf *; echo "went to town'
# Works fine if left as a top-level argument
/usr/bin/command 'The smith\'s "; rm -rf *; echo "went to town'
# BUT if put in a double-quoted string:
/usr/bin/command "subcommand1 'The smith\'s "; rm -rf *; echo "went to town'"
# it is broken into 3 shell commands:
/usr/bin/command "something and 'The smith\'s ";
rm -rf *;
echo "went to town'"
# bad day...
What about using heredoc?
$str = <<<'EOD'
/usr/local/bin/exiv2 -M "set Iptc.Application2.Caption String $options" modify IMG.jpg
EOD;
exec($str);
To modify this to use excapeshellcmd():
$options = excapeshellcmd($caption);
$command = <<<'EOD'
/usr/local/bin/exiv2 -M"set Iptc.Application2.Caption String $options" modify IMG.jpg
EOD;
exec($command);
Because of Exiv2's non-standard shell arguments, it is not easy to reach a simple and robust solution to handle user-supplied quotes correctly. There is another solution that is likely to be far more reliable and easy to maintain with a small performance penalty.
Write the Exiv2 instructions to a file cmds.txt, then call:
exiv2 -m cmds.txt IMG.jpg
to read the instructions from the file.
Update: I have implemented this method and it requires no escaping of the user-supplied data. This data is written directly to a text file which is read in by Exiv2. The Exiv2 command file format is very simple and newline-terminated, allow no escaping within values, so all I need to do is prevent newlines from passing through, which I was not allowing anyway.

How may I pass "^|" as an argument for an executable?

Within a PHP environment I am trying to execute sift (a grep-like program) to return filenames if either of the two strings matches.
// To return filenames having either string1 OR string2.
<?php
$cmd='sift.exe -l --blocksize=5000000 -Q string1^|string2 dir';
exec ($cmd);
?>
I am guessing I am unable to pass the OR operator (^|) correctly.
For matching a single string it works fine:
// To return filenames having string1
<?php
$cmd='sift.exe -l --blocksize=5000000 -Q string1 dir';
exec ($cmd);
?>
Any advice please?
The pipe symbol is being interpreted by the shell that executes your command. You either need to escape the pipe symbol, or quote the pattern argument.
Here are two solutions that work:
$cmd='sift.exe -l --blocksize=5000000 -Q string1^\|string2 dir';
$cmd="sift.exe -l --blocksize=5000000 -Q 'string1^|string2' dir";
As Bill Karwin pointed out you have to escape some characters in your command.
PHP has escapeshellcmd function to take care of that, so you code should look like:
$cmd = 'sift.exe -l --blocksize=5000000 -Q string1^|string2 dir';
$escapedCommand = escapeshellcmd($cmd);
exec ($escapedCommand);
Optionally you can use escapeshellarg function.
Look at this question to see what characters need to be escaped.

How to use special characters inside php exec

Can anyone explain how to make this code work?
exec("ffmpeg -f concat -i <(printf "file '%s'\n" ./*.mp4) -c copy output.mp4");
I've tried to add '\' before " and ', it still didn't work.
Appreciate any help. Thanks!
You must escape double quotes and backslashes. You can do it using backslash: \
exec("ffmpeg -f concat -i <(printf \"file '%s'\\n\" ./*.mp4) -c copy output.mp4");
i know this question is super old but i had the same problem myself and couldn't find the answer anywhere
in case you are having this error on windows, to escape the special characters you have to use ^ instead of backslash
so stuff like
exec("some_application https://example.com/api.php?arg1=1&arg2=2")
won't work because of the & character, so it must become
exec("some_application https://example.com/api.php?arg1=1^&arg2=2")

Using str_replace to remove spaces for exec()

I'm running into an issue with the function str_replace() while trying to create a shell command in PHP. In shell, when I tab-finish a line that has spaces, it comes up like this:
tar cvfz destination/filename.zip source/Name\ Of\ The\+\ Folder
So to me, this says that in order to run this command via exec(), I need to replace any spaces I would have in a string in PHP. To remedy this problem, I'm using
$q = str_replace(' ', '\ ' , $q);
at the start of my string parse, in order to format the spaces into "\ " instead of " ". The issue I'm having is that for this particular folder, it's also removing the plus symbol as well, and it formats it like this:
tar cvfz destination/Name\ Of\ The\ \ \ Folder.tgz source/Name\ Of\ The\ \ \ Folder
How can I set this up so str_replace() doesn't remove the plus sign? From my limited tests so far, it isn't removing anything out of these: -, %, #, !, (, *, ), ^, =
Using string functions for this is very wrong and likely to leave security holes wide open.
PHP has a function called escapeshellarg() that does exactly what you need:
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. This function should be used to escape individual arguments to shell functions coming from user input.

How to escape strings in for use in bash/sh via PHP's shell_exec?

I've been calling more advanced shell commands from PHP recently using shell_exec
As my commands become more complicated, I keep experiencing errors with things not being escaped properly. I want to be able to call shell_exec('echo '.$variable) and no matter what I put in $variable it will just echo it. Some things $variable could include are $ ~ ' " \n \r \c `` ( ) { } ; \
What's the best way to escape a shell command before executing it?
Does escapeshellcmd or escapeshellarg not do what you want?
shell_exec('echo '. escapeshellarg($variable));

Categories