The PHP directive cgi.fix_pathinfo
controls how PHP interprets requests when PATH_INFO is present. By default (cgi.fix_pathinfo=1
), PHP will:
- First try to find the exact file matching the request URI
- If not found, it will progressively "trim" path components to find a valid file
For example, with a request to /test.php/nonexistent.jpg
:
1. First checks for /test.php/nonexistent.jpg (not found)
2. Then checks for /test.php (found and executed)
3. PATH_INFO becomes "/nonexistent.jpg"
The danger emerges from how Nginx passes requests to PHP-FPM. A typical vulnerable configuration looks like:
location ~ \.php$ {
fastcgi_pass unix:/var/run/php-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
Attackers can exploit this by crafting requests like:
GET /uploads/user_photo.jpg/evil.php HTTP/1.1
With cgi.fix_pathinfo=1
, PHP will:
- Find
/uploads/user_photo.jpg
(existing file) - Execute it as PHP code because of the
.php
suffix in PATH_INFO
The most secure approach is to set cgi.fix_pathinfo=0
in php.ini. However, if you need PATH_INFO support, use this Nginx configuration:
location ~ [^/]\.php(/|$) {
fastcgi_split_path_info ^(.+?\.php)(/.+)$;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_pass unix:/var/run/php-fpm.sock;
include fastcgi_params;
# Additional security measures
try_files $uri =404;
fastcgi_intercept_errors on;
}
Using Unix sockets instead of TCP connections adds filesystem permission checks, but doesn't eliminate the core vulnerability. The real protection comes from:
- Proper file permissions (0750 for directories, 0640 for files)
- Disabling
cgi.fix_pathinfo
- Using
try_files $uri =404
in Nginx
Apache handles PATH_INFO differently by default, making it less vulnerable to this specific attack. However, similar issues can occur with misconfigured handlers. The key differences:
- Apache's mod_php doesn't use FastCGI
- Apache typically checks file existence before processing
- Most Apache configurations don't execute arbitrary files as PHP
While modern Nginx and PHP-FPM versions have improved security:
- PHP 7.0+ has better default security settings
- Nginx 1.10+ includes better FastCGI handling
- The vulnerability still exists if misconfigured
Always test your configuration with:
curl -I http://yoursite.com/uploads/test.jpg/index.php
And verify that it either:
- Returns 404 (secure)
- Returns the JPEG with Content-Type: image/jpeg (acceptable)
- Does NOT return a 200 with PHP execution
The PHP directive cgi.fix_pathinfo
controls how PHP interprets the PATH_INFO
and SCRIPT_FILENAME
variables in CGI environments. When enabled (set to 1), PHP will:
// Example of vulnerable interpretation:
Request URL: /script.php/non-existent-path
// PHP may execute script.php while ignoring the invalid trailing path
This becomes dangerous with Nginx's default FastCGI configuration when handling PHP files:
location ~ \.php$ {
fastcgi_pass unix:/var/run/php-fpm.sock;
include fastcgi_params;
# Vulnerable if cgi.fix_pathinfo=1
}
Attack scenario:
- Attacker uploads malicious image with PHP code (e.g., evil.jpg)
- Requests the file as evil.jpg/script.php
- Nginx passes the request to PHP-FPM
- With cgi.fix_pathinfo=1, PHP executes evil.jpg as PHP code
Current best practices for Nginx configurations:
location ~ \.php$ {
fastcgi_pass unix:/var/run/php-fpm.sock;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_index index.php;
try_files $uri =404; # Critical security measure
}
Essential security measures:
- Set
cgi.fix_pathinfo=0
in php.ini - Implement
try_files $uri =404
to verify file existence - Use separate storage for user uploads with no execution permissions
Apache is generally less vulnerable because:
- Its default handler for PHP files doesn't process PATH_INFO the same way
- mod_php executes scripts based on actual file extensions
- Typical .htaccess rules prevent execution of uploaded files
While using Unix sockets improves security by:
- Eliminating network exposure
- Providing filesystem permission controls
It doesn't fundamentally solve the pathinfo issue - you still need proper configuration.
Modern PHP versions (7.4+) have better default security:
- New installations typically set cgi.fix_pathinfo=0
- PHP-FPM has improved request validation
- Nginx documentation now provides secure examples