PHP as CGI paper
This commit is contained in:
parent
e3827efdee
commit
86be99fc43
|
@ -16,6 +16,7 @@ Computer Nerd Stuff
|
|||
|
||||
* [Runit on Arch Linux](arch-runit.html)
|
||||
* [Reply-To Munging Still Considered Harmful](reply-to-still-harmful.html)
|
||||
* [Runnning PHP as a CGI](php-cgi.html) in anything other than Apache
|
||||
* [Converting .docx files to text using unzip and sed](docx.html)
|
||||
* [Introduction to TCP Sockets](sockets.html)
|
||||
* [3-Minute HTML Tutorial](html-tutorial.html)
|
||||
|
|
|
@ -0,0 +1,161 @@
|
|||
Title: Running PHP as a CGI
|
||||
|
||||
I'm the author of the
|
||||
[eris HTTPd](http://woozle.org/~neale/src/eris.html),
|
||||
a small web server intended for use on embedded Linux devices with low RAM and low storage.
|
||||
I've used other web servers (boa, mathopd, thttpd, etc.) for years,
|
||||
and this problem has been present for as long as I can remember.
|
||||
A [recent gripe post about PHP](http://me.veekun.com/blog/2012/04/09/php-a-fractal-of-bad-design/)
|
||||
inspired me to document it.
|
||||
|
||||
The Situation
|
||||
-------------
|
||||
|
||||
Let's say I'm using something that isn't Apache,
|
||||
and I need to run some PHP code.
|
||||
PHP comes with a CGI variant,
|
||||
so I'll use that:
|
||||
|
||||
#! /usr/bin/php-cgi
|
||||
<?php phpinfo(); ?>
|
||||
|
||||
When run from the command-line, this does exactly what I expect.
|
||||
Superb!
|
||||
When I try it from a browser, though, I get:
|
||||
|
||||
Unable to load the webpage because the server sent no data.
|
||||
|
||||
Well that's puzzling.
|
||||
So I do my standard thing,
|
||||
wrapping it with something to dump stderr to stdout.
|
||||
|
||||
#! /bin/sh
|
||||
echo "Content-type: text/plain"
|
||||
echo
|
||||
exec foo.php.cgi 2>&1
|
||||
|
||||
Now I get this (reformatted for narrow displays):
|
||||
|
||||
<b>Security Alert!</b> The PHP CGI cannot
|
||||
be accessed directly.
|
||||
|
||||
<p>This PHP CGI binary was compiled with
|
||||
force-cgi-redirect enabled. This means
|
||||
that a page will only be served up if the
|
||||
REDIRECT_STATUS CGI variable is set, e.g.
|
||||
via an Apache Action directive.</p>
|
||||
<p>For more information as to <i>why</i>
|
||||
this behaviour exists, see the
|
||||
<a href="http://php.net/security.cgi-bin">manual
|
||||
page for CGI security</a>.</p>
|
||||
<p>For more information about changing this
|
||||
behaviour or re-enabling this webserver,
|
||||
consult the installation file that came with
|
||||
this distribution, or visit
|
||||
<a href="http://php.net/install.windows">the
|
||||
manual page</a>.</p>
|
||||
|
||||
It's writing out HTML, sort of: I mean, the first paragraph doesn't have P tags, but whatever.
|
||||
So clearly they intend me to see it from a browser.
|
||||
I mean, the interpreter binary is called `php-cgi`, after all.
|
||||
And yet, they're not actually writing out a CGI header,
|
||||
so the web server thinks it's a broken script and dies.
|
||||
So maybe they meant me to view this from the command-line.
|
||||
Except, this error only shows up when I run it as CGI.
|
||||
|
||||
Anyway.
|
||||
|
||||
PHP is asking me to set the REDIRECT_STATUS variable,
|
||||
and telling me that this is somehow a security concern.
|
||||
It's interesting how Python, Perl, and Lua don't think the lack of this variable is a security problem.
|
||||
|
||||
A quick Google search tells me this is a non-standard CGI variable that Apache sets.
|
||||
But I'm not using Apache, otherwise I'd just use mod_php.
|
||||
Why are you making me set this, PHP?
|
||||
|
||||
What I thought would fix it
|
||||
---------------------------
|
||||
|
||||
Okay, whatever.
|
||||
|
||||
#! /bin/sh
|
||||
echo "Content-type: text/plain"
|
||||
echo
|
||||
REDIRECT_STATUS=1 export REDIRECT_STATUS
|
||||
exec foo.php.cgi 2>&1
|
||||
|
||||
Now I get this:
|
||||
|
||||
Status: 404 Not Found
|
||||
X-Powered-By: PHP/5.3.3-7+squeeze15
|
||||
Content-type: text/html
|
||||
|
||||
No input file specified.
|
||||
|
||||
However, it continues to run just fine from the command-line.
|
||||
|
||||
404 means "file not found".
|
||||
Which is very strange, because clearly the file *was* found,
|
||||
given that PHP has sent its "X-Powered-By" header.
|
||||
If it couldn't find the file, it wouldn't have known enough to start php-cgi.
|
||||
And, remember, this all works fine from the command line.
|
||||
Not to mention Python, Perl, Lua, and even the lowly Awk and Bourne shell,
|
||||
have no trouble finding their input file when run as a CGI with the shebang (#! /path/to/binary)
|
||||
as the first line.
|
||||
|
||||
What actually fixed it
|
||||
----------------------
|
||||
|
||||
After nearly a full day trying to chase this cryptic message down in web searches,
|
||||
I landed on a PHP bug open since 2004:
|
||||
[PHP CGI depends on non-standard SCRIPT_FILENAME](https://bugs.php.net/bug.php?id=28227).
|
||||
Included in the comments on this ancient but still unresolved bug is a link to
|
||||
[a wrapper](http://pastebin.ca/1296199)
|
||||
which proports to fix the problem.
|
||||
|
||||
So the ultimate fix to make `php-cgi` actually run like a CGI is to wrap it
|
||||
with a second CGI that convinces PHP that I really meant it:
|
||||
|
||||
#! /bin/sh
|
||||
REDIRECT_STATUS=1 export REDIRECT_STATUS
|
||||
SCRIPT_FILENAME=$(pwd)/foo.php.cgi export SCRIPT_FILENAME
|
||||
exec $SCRIPT_FILENAME 2>&1
|
||||
|
||||
If I have multiple PHP CGIs,
|
||||
I must wrap each one.
|
||||
Or just give up and install Apache,
|
||||
which is, I'm sure,
|
||||
the path taken by most system administrators who haven't written their own web server.
|
||||
|
||||
Why does PHP do this?
|
||||
---------------------
|
||||
|
||||
I have skimmed [the URL that they asked me to](http://us3.php.net/manual/en/security.cgi-bin.attacks.php).
|
||||
They list two points:
|
||||
|
||||
1. "Interpreters open and execute the file specified as the first argument on the command line." This is true, it's how shebangs work (a file "script.sh" beginning with "#!/bin/sh" is magically transformed to ["/bin/sh", "script.sh"]). It's how Python and Perl launch. I don't get the exploit path here, unless there's some horrible way to misconfigure Apache to do the wrong thing with scripts.
|
||||
|
||||
2. If you put the php interpreter under your web server's document root (where files are served from), and then use it to load up php scripts using the `PATH_INFO` environment variable *which is specified by the end user*, then PHP will dutifully serve up every document on your machine.
|
||||
|
||||
If you don't get it, (I didn't either), what they mean is that
|
||||
some sysadmins honestly want the following URL to work:
|
||||
|
||||
http://yourhost/cgi-bin/php-cgi/var/www/yourhost-root/scripts/mything.php
|
||||
|
||||
This is such a terrible way to do things that it took me half an hour to even understand what they were describing. It's analagous to putting your shell in `/var/www/cgi-bin` and then asking the browser (which, remember, the end user can make do whatever they please) to pass the path to your shell script in as part of the URL.
|
||||
|
||||
|
||||
These are both just awful, terrible,
|
||||
hideous ways to set up a web server.
|
||||
But apparently people do it anyway,
|
||||
and apparently the PHP developers felt like it was their job
|
||||
to allow this setup and still try to prevent
|
||||
some of the security nightmares it entails.
|
||||
Their solution: introduce dependencies on two Apache-specific
|
||||
environment variables,
|
||||
and one of them (`REDIRECT_STATUS`)
|
||||
isn't even checked further than "is this set to anything at all".
|
||||
|
||||
And that, dear reader, is why you must fake out PHP
|
||||
in order to get it to behave like every other CGI ever written.
|
||||
|
|
@ -38,6 +38,6 @@ Differences with fnord
|
|||
Download
|
||||
--------
|
||||
|
||||
* [Version 3.1.2](http://woozle.org/~neale/g.cgi/eris/snapshot/eris-3.1.2.tar.gz)
|
||||
* [Version 4.1](http://woozle.org/~neale/g.cgi/eris/snapshot/eris-4.1.tar.gz)
|
||||
* [Source Control](http://woozle.org/~neale/g.cgi/eris)
|
||||
|
||||
|
|
Loading…
Reference in New Issue