mirror of
https://github.com/php/php-src.git
synced 2026-03-24 00:02:20 +01:00
Fix GH-17645: FPM with httpd ProxyPass does not decode script path
This changes make FPM always decode SCRIPT_FILENAME when Apache ProxyPass or ProxyPassMatch is used. It also introduces a new INI option fastcgi.script_path_encoded that allows using the previous behavior of not decoding the path. The INI is introduced because there is a chance that some users could use encoded file paths in their file system as a workaround for the previous behavior. Close GH-17896
This commit is contained in:
4
NEWS
4
NEWS
@@ -62,6 +62,10 @@ PHP NEWS
|
||||
- Fileinfo:
|
||||
. Upgrade to file 5.46. (nielsdos)
|
||||
|
||||
- FPM:
|
||||
. Fixed GH-17645 (FPM with httpd ProxyPass does not decode script path).
|
||||
(Jakub Zelenka)
|
||||
|
||||
- GD:
|
||||
. Fixed bug #68629 (Transparent artifacts when using imagerotate). (pierre,
|
||||
cmb)
|
||||
|
||||
@@ -152,6 +152,10 @@ PHP 8.5 UPGRADE NOTES
|
||||
. Added a new --ini=diff option to print INI settings changed from the builtin
|
||||
default.
|
||||
|
||||
- FPM:
|
||||
. FPM with httpd ProxyPass decodes the full scirpt path script path. Added
|
||||
fastcgi.script_path_encoded INI setting to prevent this new behevior.
|
||||
|
||||
========================================
|
||||
4. Deprecated Functionality
|
||||
========================================
|
||||
|
||||
@@ -817,6 +817,12 @@ enable_dl = Off
|
||||
; https://php.net/fastcgi.impersonate
|
||||
;fastcgi.impersonate = 1
|
||||
|
||||
; Prevent decoding of SCRIPT_FILENAME when using Apache ProxyPass or
|
||||
; ProxyPassMatch. This should only be used if script file paths are already
|
||||
; stored in an encoded format on the file system.
|
||||
; Default is 0.
|
||||
;fastcgi.script_path_encoded = 1
|
||||
|
||||
; Disable logging through FastCGI connection. PHP's default behavior is to enable
|
||||
; this feature.
|
||||
;fastcgi.logging = 0
|
||||
|
||||
@@ -819,6 +819,12 @@ enable_dl = Off
|
||||
; https://php.net/fastcgi.impersonate
|
||||
;fastcgi.impersonate = 1
|
||||
|
||||
; Prevent decoding of SCRIPT_FILENAME when using Apache ProxyPass or
|
||||
; ProxyPassMatch. This should only be used if script file paths are already
|
||||
; stored in an encoded format on the file system.
|
||||
; Default is 0.
|
||||
;fastcgi.script_path_encoded = 1
|
||||
|
||||
; Disable logging through FastCGI connection. PHP's default behavior is to enable
|
||||
; this feature.
|
||||
;fastcgi.logging = 0
|
||||
|
||||
@@ -144,6 +144,7 @@ typedef struct _php_cgi_globals_struct {
|
||||
bool nph;
|
||||
bool fix_pathinfo;
|
||||
bool discard_path;
|
||||
bool fcgi_script_path_encoded;
|
||||
bool fcgi_logging;
|
||||
bool fcgi_logging_request_started;
|
||||
HashTable user_config_cache;
|
||||
@@ -1066,6 +1067,10 @@ static void init_request_info(void)
|
||||
}
|
||||
}
|
||||
|
||||
if (apache_was_here && !CGIG(fcgi_script_path_encoded)) {
|
||||
php_raw_url_decode(env_script_filename, strlen(env_script_filename));
|
||||
}
|
||||
|
||||
if (CGIG(fix_pathinfo)) {
|
||||
struct stat st;
|
||||
char *real_path = NULL;
|
||||
@@ -1174,7 +1179,7 @@ static void init_request_info(void)
|
||||
* it is probably also in SCRIPT_NAME and need to be removed
|
||||
*/
|
||||
size_t decoded_path_info_len = 0;
|
||||
if (strchr(path_info, '%')) {
|
||||
if (CGIG(fcgi_script_path_encoded) && strchr(path_info, '%')) {
|
||||
decoded_path_info = estrdup(path_info);
|
||||
decoded_path_info_len = php_raw_url_decode(decoded_path_info, strlen(path_info));
|
||||
}
|
||||
@@ -1421,13 +1426,14 @@ static void fastcgi_ini_parser(zval *arg1, zval *arg2, zval *arg3, int callback_
|
||||
/* }}} */
|
||||
|
||||
PHP_INI_BEGIN()
|
||||
STD_PHP_INI_BOOLEAN("cgi.rfc2616_headers", "0", PHP_INI_ALL, OnUpdateBool, rfc2616_headers, php_cgi_globals_struct, php_cgi_globals)
|
||||
STD_PHP_INI_BOOLEAN("cgi.nph", "0", PHP_INI_ALL, OnUpdateBool, nph, php_cgi_globals_struct, php_cgi_globals)
|
||||
STD_PHP_INI_BOOLEAN("cgi.fix_pathinfo", "1", PHP_INI_SYSTEM, OnUpdateBool, fix_pathinfo, php_cgi_globals_struct, php_cgi_globals)
|
||||
STD_PHP_INI_BOOLEAN("cgi.discard_path", "0", PHP_INI_SYSTEM, OnUpdateBool, discard_path, php_cgi_globals_struct, php_cgi_globals)
|
||||
STD_PHP_INI_BOOLEAN("fastcgi.logging", "1", PHP_INI_SYSTEM, OnUpdateBool, fcgi_logging, php_cgi_globals_struct, php_cgi_globals)
|
||||
STD_PHP_INI_ENTRY("fastcgi.error_header", NULL, PHP_INI_SYSTEM, OnUpdateString, error_header, php_cgi_globals_struct, php_cgi_globals)
|
||||
STD_PHP_INI_ENTRY("fpm.config", NULL, PHP_INI_SYSTEM, OnUpdateString, fpm_config, php_cgi_globals_struct, php_cgi_globals)
|
||||
STD_PHP_INI_BOOLEAN("cgi.rfc2616_headers", "0", PHP_INI_ALL, OnUpdateBool, rfc2616_headers, php_cgi_globals_struct, php_cgi_globals)
|
||||
STD_PHP_INI_BOOLEAN("cgi.nph", "0", PHP_INI_ALL, OnUpdateBool, nph, php_cgi_globals_struct, php_cgi_globals)
|
||||
STD_PHP_INI_BOOLEAN("cgi.fix_pathinfo", "1", PHP_INI_SYSTEM, OnUpdateBool, fix_pathinfo, php_cgi_globals_struct, php_cgi_globals)
|
||||
STD_PHP_INI_BOOLEAN("cgi.discard_path", "0", PHP_INI_SYSTEM, OnUpdateBool, discard_path, php_cgi_globals_struct, php_cgi_globals)
|
||||
STD_PHP_INI_BOOLEAN("fastcgi.script_path_encoded", "0", PHP_INI_SYSTEM, OnUpdateBool, fcgi_script_path_encoded, php_cgi_globals_struct, php_cgi_globals)
|
||||
STD_PHP_INI_BOOLEAN("fastcgi.logging", "1", PHP_INI_SYSTEM, OnUpdateBool, fcgi_logging, php_cgi_globals_struct, php_cgi_globals)
|
||||
STD_PHP_INI_ENTRY("fastcgi.error_header", NULL, PHP_INI_SYSTEM, OnUpdateString, error_header, php_cgi_globals_struct, php_cgi_globals)
|
||||
STD_PHP_INI_ENTRY("fpm.config", NULL, PHP_INI_SYSTEM, OnUpdateString, fpm_config, php_cgi_globals_struct, php_cgi_globals)
|
||||
PHP_INI_END()
|
||||
|
||||
/* {{{ php_cgi_globals_ctor */
|
||||
@@ -1437,6 +1443,7 @@ static void php_cgi_globals_ctor(php_cgi_globals_struct *php_cgi_globals)
|
||||
php_cgi_globals->nph = 0;
|
||||
php_cgi_globals->fix_pathinfo = 1;
|
||||
php_cgi_globals->discard_path = 0;
|
||||
php_cgi_globals->fcgi_script_path_encoded = 0;
|
||||
php_cgi_globals->fcgi_logging = 1;
|
||||
php_cgi_globals->fcgi_logging_request_started = false;
|
||||
zend_hash_init(&php_cgi_globals->user_config_cache, 0, NULL, user_config_cache_entry_dtor, 1);
|
||||
|
||||
54
sapi/fpm/tests/fcgi-env-pif-apache-pp-sfp-decoding.phpt
Normal file
54
sapi/fpm/tests/fcgi-env-pif-apache-pp-sfp-decoding.phpt
Normal file
@@ -0,0 +1,54 @@
|
||||
--TEST--
|
||||
FPM: FastCGI change for Apache ProxyPass SCRIPT_FILENAME decoding (GH-17645)
|
||||
--SKIPIF--
|
||||
<?php include "skipif.inc"; ?>
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
require_once "tester.inc";
|
||||
|
||||
$cfg = <<<EOT
|
||||
[global]
|
||||
error_log = {{FILE:LOG}}
|
||||
[unconfined]
|
||||
listen = {{ADDR}}
|
||||
pm = dynamic
|
||||
pm.max_children = 5
|
||||
pm.start_servers = 1
|
||||
pm.min_spare_servers = 1
|
||||
pm.max_spare_servers = 3
|
||||
php_admin_value[cgi.fix_pathinfo] = yes
|
||||
EOT;
|
||||
|
||||
$code = <<<EOT
|
||||
<?php
|
||||
echo \$_SERVER["SCRIPT_NAME"] . "\n";
|
||||
echo \$_SERVER["ORIG_SCRIPT_NAME"] . "\n";
|
||||
echo \$_SERVER["SCRIPT_FILENAME"] . "\n";
|
||||
echo \$_SERVER["PATH_INFO"] . "\n";
|
||||
echo \$_SERVER["PHP_SELF"];
|
||||
EOT;
|
||||
|
||||
$tester = new FPM\Tester($cfg, $code);
|
||||
[$sourceFilePath, $scriptName] = $tester->createSourceFileAndScriptName('+');
|
||||
$tester->start();
|
||||
$tester->expectLogStartNotices();
|
||||
$tester
|
||||
->request(
|
||||
uri: $scriptName . '/1%202',
|
||||
scriptFilename: "proxy:fcgi://" . $tester->getAddr() . str_replace('+', '%2B', $sourceFilePath) . '/1%202',
|
||||
scriptName: $scriptName . '/1 2'
|
||||
)
|
||||
->expectBody([$scriptName, $scriptName . '/1 2', $sourceFilePath, '/1 2', $scriptName . '/1 2']);
|
||||
$tester->terminate();
|
||||
$tester->close();
|
||||
|
||||
?>
|
||||
Done
|
||||
--EXPECT--
|
||||
Done
|
||||
--CLEAN--
|
||||
<?php
|
||||
require_once "tester.inc";
|
||||
FPM\Tester::clean();
|
||||
?>
|
||||
55
sapi/fpm/tests/fcgi-env-pif-apache-pp-sfp-encoded.phpt
Normal file
55
sapi/fpm/tests/fcgi-env-pif-apache-pp-sfp-encoded.phpt
Normal file
@@ -0,0 +1,55 @@
|
||||
--TEST--
|
||||
FPM: FastCGI change for Apache ProxyPass SCRIPT_FILENAME decoding - fallback (GH-17645)
|
||||
--SKIPIF--
|
||||
<?php include "skipif.inc"; ?>
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
require_once "tester.inc";
|
||||
|
||||
$cfg = <<<EOT
|
||||
[global]
|
||||
error_log = {{FILE:LOG}}
|
||||
[unconfined]
|
||||
listen = {{ADDR}}
|
||||
pm = dynamic
|
||||
pm.max_children = 5
|
||||
pm.start_servers = 1
|
||||
pm.min_spare_servers = 1
|
||||
pm.max_spare_servers = 3
|
||||
php_admin_value[cgi.fix_pathinfo] = yes
|
||||
php_admin_value[fastcgi.script_path_encoded] = yes
|
||||
EOT;
|
||||
|
||||
$code = <<<EOT
|
||||
<?php
|
||||
echo \$_SERVER["SCRIPT_NAME"] . "\n";
|
||||
echo \$_SERVER["ORIG_SCRIPT_NAME"] . "\n";
|
||||
echo \$_SERVER["SCRIPT_FILENAME"] . "\n";
|
||||
echo \$_SERVER["PATH_INFO"] . "\n";
|
||||
echo \$_SERVER["PHP_SELF"];
|
||||
EOT;
|
||||
|
||||
$tester = new FPM\Tester($cfg, $code);
|
||||
[$sourceFilePath, $scriptName] = $tester->createSourceFileAndScriptName('%2B');
|
||||
$tester->start();
|
||||
$tester->expectLogStartNotices();
|
||||
$tester
|
||||
->request(
|
||||
uri: $scriptName . '/1%202',
|
||||
scriptFilename: "proxy:fcgi://" . $tester->getAddr() . $sourceFilePath . '/1%202',
|
||||
scriptName: $scriptName . '/1 2'
|
||||
)
|
||||
->expectBody([$scriptName, $scriptName . '/1 2', $sourceFilePath, '/1 2', $scriptName . '/1 2']);
|
||||
$tester->terminate();
|
||||
$tester->close();
|
||||
|
||||
?>
|
||||
Done
|
||||
--EXPECT--
|
||||
Done
|
||||
--CLEAN--
|
||||
<?php
|
||||
require_once "tester.inc";
|
||||
FPM\Tester::clean();
|
||||
?>
|
||||
@@ -47,7 +47,7 @@ class Tester
|
||||
self::FILE_EXT_LOG_ERR,
|
||||
self::FILE_EXT_LOG_SLOW,
|
||||
self::FILE_EXT_PID,
|
||||
'src.php',
|
||||
'*src.php',
|
||||
'ini',
|
||||
'skip.ini',
|
||||
'*.sock',
|
||||
@@ -107,6 +107,11 @@ class Tester
|
||||
*/
|
||||
private string $fileName;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private ?string $sourceFile = null;
|
||||
|
||||
/**
|
||||
* @var resource
|
||||
*/
|
||||
@@ -1483,21 +1488,27 @@ class Tester
|
||||
/**
|
||||
* Create a source code file.
|
||||
*
|
||||
* @param string $nameSuffix
|
||||
* @return string
|
||||
*/
|
||||
public function makeSourceFile(): string
|
||||
public function makeSourceFile(string $nameSuffix = ''): string
|
||||
{
|
||||
return $this->makeFile('src.php', $this->code, overwrite: false);
|
||||
if (is_null($this->sourceFile)) {
|
||||
$this->sourceFile = $this->makeFile($nameSuffix . 'src.php', $this->code, overwrite: false);
|
||||
}
|
||||
|
||||
return $this->sourceFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a source file and script name.
|
||||
*
|
||||
* @param string $nameSuffix
|
||||
* @return string[]
|
||||
*/
|
||||
public function createSourceFileAndScriptName(): array
|
||||
public function createSourceFileAndScriptName(string $nameSuffix = ''): array
|
||||
{
|
||||
$sourceFile = $this->makeFile('src.php', $this->code, overwrite: false);
|
||||
$sourceFile = $this->makeSourceFile($nameSuffix);
|
||||
|
||||
return [$sourceFile, '/' . basename($sourceFile)];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user