php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #68053 PHP-FPM with cgi.fix_pathinfo 0 loads script from PATH_TRANSLATED
Submitted: 2014-09-19 10:44 UTC Modified: 2023-04-06 14:41 UTC
Votes:4
Avg. Score:4.2 ± 0.8
Reproduced:2 of 2 (100.0%)
Same Version:0 (0.0%)
Same OS:1 (50.0%)
From: devel at jasonwoods dot me dot uk Assigned: bukka (profile)
Status: Wont fix Package: FPM related
PHP Version: 5.5.17 OS: CentOS 6.5
Private report: No CVE-ID: None
Welcome back! If you're the original bug submitter, here's where you can edit the bug or add additional notes.
If you forgot your password, you can retrieve your password here.
Password:
Status:
Package:
Bug Type:
Summary:
From: devel at jasonwoods dot me dot uk
New email:
PHP Version: OS:

 

 [2014-09-19 10:44 UTC] devel at jasonwoods dot me dot uk
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

Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2014-09-19 10:45 UTC] devel at jasonwoods dot me dot uk
Originally raised as #67164 but was private, so we can delete/close/duplicate #67164 and keep this public one - I'm keen if anyone else experience this or knows what is "right"
 [2023-04-06 14:41 UTC] bukka@php.net
-Status: Open +Status: Wont fix -Assigned To: +Assigned To: bukka
 [2023-04-06 14:41 UTC] bukka@php.net
I have been looking into this and agree that the behaviour is a bit unfortunate. However I don't think we can fix it all at this stage as it could result in hard to find a BC break as some application might rely on the PATH_TRANSLATED being used and PHP_SELF having always PATH_INFO value if set. I don't think we can fix the PATH_TRANSLATED at all and don't see it as a big issue as there is still cgi.discard_path = 1. There is a better case for PHP_SELF though and I think we should address it as an improvement so we limit the impact of its BC break. For that I created following issue: https://github.com/php/php-src/issues/11025

Closing this as won't fix even though the PHP_SELF will be likely addressed but that's just part of this report and will be more an improvement as I said.
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Sat Dec 21 13:01:31 2024 UTC