[php-src] Issue #15246: PHP-FPM + Apache: special characters in SCRIPT_FILENAME are URL encoded
| From: | abgandar | Date: | Mon, 05 Aug 2024 10:47:32 +0000 |
| Subject: | [php-src] Issue #15246: PHP-FPM + Apache: special characters in SCRIPT_FILENAME are URL encoded | ||
| Groups: | php.bugs | ||
| Request: | Send a blank email to php-bugs+get-247435@lists.php.net to get a copy of this message | ||
Issue: https://github.com/php/php-src/issues/15246
Author: abgandar
### Description
### Problem
I use Apache + PHP-FPM to serve PHP files sitting in the server's document root. Some PHP files
have spaces and UTF-8 encoded special characters (umlauts, accents) in their file names. Files
without spaces or umlauts are served correctly, but files with any of those lead to a 404 with
PHP-FPM responding 'Primary script unknown'.
This appears to be due to the way Apache passes the SCRIPT_FILENAME variable: it seems to prefix the
physical file name by proxy:fcgi:// and then URL encodes the file name following. So the correctly
resolved file name "/docroot/my file.php" gets passed as
"proxy:fcgi://docroot/my%20file.php". PHP-FPM removes the prefix, but then tries to find
the file "/docroot/my%20file.php", which doesn't exist.
This seems to me to be related to #12996: the fix to that bug was to URL decode PATH_INFO, but it
does not cover the case where the physical script file (SCRIPT_FILENAME) is also URL encoded. So it
just URL decodes the bit after the file name.
### Symptoms
Using the following PHP-FPM access log format
```
access.format = "%R - %u %t \"%m %r%Q%q\" %s %f %{ORIG_SCRIPT_FILENAME}e"
```
when requesting the URL
```
https://my.server/files/test%20me.php
```
I get logs like this
```
- - 04/Aug/2024:21:35:48 +0200 "GET /files/test me.php" 404 -
/var/htdocs/files/test%20me.php
```
Note the request (%r) is URL decoded, but the ORIG_SCRIPT_FILENAME is not. The file
"/var/htdocs/files/test me.php" exists and is readable by the php-fpm user. Files without
the space work fine. I use ORIG_SCRIPT_FILENAME because PHP-FPM unsets SCRIPT_FILENAME when
reporting a 404 error (hence the empty %f), but saves the value in ORIG_SCRIPT_FILENAME.
### Setup
I'm using PHP 8.3.10 with Apache/2.4.62 (FreeBSD) OpenSSL/3.2.2 on FreeBSD 14.1-STABLE (both
built from ports).
Relevant Apache config:
```
<FilesMatch "\.php$">
SetHandler "proxy:unix:/var/run/php-fpm.sock|fcgi://php-fpm"
</FilesMatch>
<Proxy "fcgi://php-fpm" enablereuse=off max=18>
</Proxy>
# PHP Apache FCGI hack to pass HTTP AUTH info
ProxyFCGISetEnvIf "%{HTTP:Authorization} =~ /(.+)/" HTTP_AUTHORIZATION "$1"
ProxyFCGISetEnvIf "%{REMOTE_USER} =~ /(.+)/" PHP_AUTH_USER "$1"
```
My PHP-FPM setup is stock with UNIX socket at /var/run/php-fpm.sock and access logging as per above.
I have had this setup unchanged for several years and PHP and Apache versions. I'm pretty sure
this worked about half a year ago, but can't say when precisely this broke (and if it was a
change on the Apache or PHP side).
### Workaround
As a workaround, I can make everything run just fine by forcing Apache to unescape the
SCRIPT_FILENAME variable before passing it to PHP-FPM:
```
ProxyFCGISetEnvIf "true" SCRIPT_FILENAME "%{unescape:%{SCRIPT_FILENAME}}"
```
I don't know what that does to PATH_INFO stuff as I don't use that.
### Solution(?)
Since fpm_main.c already does a lot of gymnastics to fix Apache's botched inputs, it may be
better to unescape the SCRIPT_FILENAME when removing the "proxy:fcgi://" prefix in
[fpm_main.c](https://github.com/php/php-src/blob/50217b35ea678b585b26e4edf30844f198017b22/sapi/fpm/fpm/fpm_main.c#L1018C35-L1018C48).
Unfortunately, I don't fully understand all the logic there, and am not familiar enough with
PHP sources to confidently propose a fix.
### PHP Version
PHP 8.3.10
### Operating System
FreeBSD 14.1-STABLE