Bug #68053 [NEW]: PHP-FPM with cgi.fix_pathinfo 0 loads script from PATH_TRANSLATED
| From: | devel at jasonwoods dot me dot uk | Date: | Fri, 19 Sep 2014 10:44:34 +0000 |
| Subject: | Bug #68053 [NEW]: PHP-FPM with cgi.fix_pathinfo 0 loads script from PATH_TRANSLATED | ||
| Groups: | php.bugs | ||
| Request: | Send a blank email to php-bugs+get-187597@lists.php.net to get a copy of this message | ||
From: devel at jasonwoods dot me dot uk
Operating system: CentOS 6.5
PHP version: 5.5.17
Package: FPM related
Bug Type: Bug
Bug description:PHP-FPM with cgi.fix_pathinfo 0 loads script from PATH_TRANSLATED
Description:
------------
Looking at the nginx example config here:
http://wiki.nginx.org/HttpFcgiModule#fastcgi_split_path_info
(or even here: http://wiki.nginx.org/PHPFcgiExample)
During a request, PATH_TRANSLATED is set to the full path to the
PATH_INFO on the filesystem as defined by the CGI specification:
http://www.ietf.org/rfc/rfc3875
Now we have something like this:
location ~ [^/]\.php(/|$) {
fastcgi_split_path_info ^((?U).*[^/]\.php)(/.*)$;
include "fastcgi.conf"; # To get the other standard variables
fastcgi_param SCRIPT_FILENAME
$document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;
if (!-f $document_root$fastcgi_script_name) {
return 404;
}
fastcgi_pass unix:/var/run/php-fpm/{{ id }}.sock;
}
This works OK with cgi.fix_pathinfo=1 in the PHP-FPM configuration.
Scripts run fine as expected with PATH_INFO populated and
PATH_TRANSLATED populated. For example, requesting
www.test.com/script.php/images would load script.php.
Now, a lot of blogs (maybe incorrectly) say to disable cgi.fix_pathinfo
for security, and reading the docs it makes sense... I've done the
splitting in Nginx, why should FPM do any... However, setting it to 0
will generally result in "Access is denied!" and error_log such as the
following:
FastCGI sent in stderr: "Access to the script '<root>/images' has been
denied (see security.limit_extensions)" while reading response header
from upstream, client: X.X.X.X, server: www.test.com, request: "GET
/test.php/images HTTP/1.1", upstream:
"fastcgi://unix:/var/run/php-fpm/test.sock:", host: "www.test.com"
If you change the request to
www.test.com/script.php/images/anotherphpscript.php, and actually create
that script in <root>/images/anotherphpscript.php, it actually loads
that script! It seems that when PATH_TRANSLATED is present and
fix_pathinfo is 0, PHP loads the script from the path pointed to by
PATH_TRANSLATED and ignores SCRIPT_FILENAME completely! Yet from what I
can gather, PATH_TRANSLATED is the filesystem path to the PATH_INFO stem
- this is not meant to be the script to run!
Looking into it further though, it seems PHP documentation kind of
states this:
http://php.net/manual/en/reserved.variables.server.php
'PATH_TRANSLATED' - Filesystem- (not document root-) based path to the
current script, after the server has done any virtual-to-real mapping.
Yet looking elsewhere you get a completely different definition:
http://oreilly.com/openbook/cgi/ch02_04.html
Meanwhile, the variable PATH_TRANSLATED is also set, which maps the
information stored in PATH_INFO to the document root directory (e.g.,
/usr/local/etc/httpd/ public/cgi/cgi-doc.txt).
Have I misunderstood this completely or PHP completely loading the wrong
script?
The thing that concerns me is it appears that using configs on the Nginx
website and following a common security recommendation on the web,
essentially makes one potentially INSECURE, as it means someone could
run a PHP file in /images/ even if you have a match in nginx on /images/
that returns 401
FYI - Adding cgi.discard_path = 1 (with cgi.fix_pathinfo = 0) seems to
fix the problem though appears to be sparsely mentioned, but introduces
a new issue in that PHP_SELF remains the same and ends up the PATH_INFO
(/images/anotherphpscript.php - which would be right if it was still
running that script)
Compare to cgi.discard_path = 0 (default) and cgi.fix_pathinfo = 1 where
PHP_SELF is correct (/script.php/images/anotherphpscript.php)
Test script:
---------------
This is a really good example, who
Setup nginx with php-fpm and use following block for requests:
location ~ [^/]\.php(/|$) {
fastcgi_split_path_info ^((?U).*[^/]\.php)(/.*)$;
include "fastcgi.conf"; # To get the other standard variables
fastcgi_param SCRIPT_FILENAME
$document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;
if (!-f $document_root$fastcgi_script_name) {
return 404;
}
fastcgi_pass unix:/var/run/php-fpm/{{ id }}.sock;
}
1. Create <root>/images/anotherphpscript.php
<?php
echo "ANOTHERPHPSCRIPT.PHP";
phpinfo();
?>
2. Create <root>/script.php to output "SCRIPT.PHP"
<?php
echo "SCRIPT.PHP";
?>
3. Request www.test.com/script.php/images/anotherphpscript.php
Expected result:
----------------
When requesting www.test.com/script.php/images/anotherphpscript.php
Receive output "SCRIPT.PHP"
Actual result:
--------------
When requesting www.test.com/script.php/images/anotherphpscript.php
Receive output "ANOTHERPHPSCRIPT.PHP"
Receive PHP info dump with following _SERVER:
_SERVER["USER"] test
_SERVER["HOME"] /home/test
_SERVER["FCGI_ROLE"] RESPONDER
_SERVER["SCRIPT_FILENAME"] /home/access/site/script.php
_SERVER["QUERY_STRING"] no value
_SERVER["REQUEST_METHOD"] GET
_SERVER["CONTENT_TYPE"] no value
_SERVER["CONTENT_LENGTH"] no value
_SERVER["SCRIPT_NAME"] /script.php
_SERVER["REQUEST_URI"] /script.php/images/anotherphpscript.php
_SERVER["DOCUMENT_URI"] /script.php/images/anotherphpscript.php
_SERVER["DOCUMENT_ROOT"] /home/access/site
_SERVER["SERVER_PROTOCOL"] HTTP/1.1
_SERVER["GATEWAY_INTERFACE"] CGI/1.1
_SERVER["SERVER_SOFTWARE"] nginx/1.6.0
_SERVER["REMOTE_ADDR"] X.X.X.X
_SERVER["REMOTE_PORT"] XXXXX
_SERVER["SERVER_ADDR"] X.X.X.X
_SERVER["SERVER_PORT"] 80
_SERVER["SERVER_NAME"] www.test.com
_SERVER["REDIRECT_STATUS"] 200
_SERVER["PATH_INFO"] /images/anotherphpscript.php
_SERVER["HTTPS"] no value
_SERVER["HTTP_X_FORWARDED_FOR"] X.X.X.X
_SERVER["PATH_TRANSLATED"] /home/access/site/images/anotherphpscript.php
_SERVER["HTTP_HOST"] www.test.com
_SERVER["HTTP_DNT"] 1
_SERVER["HTTP_COOKIE"] no value
_SERVER["HTTP_CONNECTION"] keep-alive
_SERVER["HTTP_ACCEPT"] text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
_SERVER["HTTP_USER_AGENT"] Mozilla/5.0 (Macintosh; Intel Mac OS X
10_9_2) AppleWebKit/537.75.14 (KHTML, like Gecko) Version/7.0.3
Safari/537.75.14
_SERVER["HTTP_ACCEPT_LANGUAGE"] en-us
_SERVER["HTTP_ACCEPT_ENCODING"] gzip, deflate
_SERVER["HTTP_CACHE_CONTROL"] max-age=0
_SERVER["PHP_SELF"] /images/anotherphpscript.php
_SERVER["REQUEST_TIME_FLOAT"] 1398935271.6949
_SERVER["REQUEST_TIME"] 1398935271
--
Edit bug report at https://bugs.php.net/bug.php?id=68053&edit=1
--
Try a snapshot (PHP 5.4): https://bugs.php.net/fix.php?id=68053&r=trysnapshot54
Try a snapshot (PHP 5.5): https://bugs.php.net/fix.php?id=68053&r=trysnapshot55
Try a snapshot (trunk): https://bugs.php.net/fix.php?id=68053&r=trysnapshottrunk
Fixed in SVN: https://bugs.php.net/fix.php?id=68053&r=fixed
Fixed in release: https://bugs.php.net/fix.php?id=68053&r=alreadyfixed
Need backtrace: https://bugs.php.net/fix.php?id=68053&r=needtrace
Need Reproduce Script: https://bugs.php.net/fix.php?id=68053&r=needscript
Try newer version: https://bugs.php.net/fix.php?id=68053&r=oldversion
Not developer issue: https://bugs.php.net/fix.php?id=68053&r=support
Expected behavior: https://bugs.php.net/fix.php?id=68053&r=notwrong
Not enough info: https://bugs.php.net/fix.php?id=68053&r=notenoughinfo
Submitted twice: https://bugs.php.net/fix.php?id=68053&r=submittedtwice
register_globals: https://bugs.php.net/fix.php?id=68053&r=globals
PHP 4 support discontinued: https://bugs.php.net/fix.php?id=68053&r=php4
Daylight Savings: https://bugs.php.net/fix.php?id=68053&r=dst
IIS Stability: https://bugs.php.net/fix.php?id=68053&r=isapi
Install GNU Sed: https://bugs.php.net/fix.php?id=68053&r=gnused
Floating point limitations: https://bugs.php.net/fix.php?id=68053&r=float
No Zend Extensions: https://bugs.php.net/fix.php?id=68053&r=nozend
MySQL Configuration Error: https://bugs.php.net/fix.php?id=68053&r=mysqlcfg