I would not describe this change as "critical" per se, but it still has security implications. I'm not aware of anything that has fundamentally changed since that advice became relevant -- PHP's behavior tends strongly towards backwards compatibility, even when that has security implications. Imagine you have a server that allows people to upload files to the server, say in a directory called upload
. Now, your upload script is careful to only allow files with certain extensions (say only .png
) to ensure nobody uploads a malicious PHP file.
Now, as an attacker, I'll try to write a PHP shell into a file, name the file evil.png
, and upload it. When it uploads, I'll visit http://example.com/upload/evil.png
, but I'll discover that this just downloads the file to me -- nginx never dispatched the request to php-fcgi to be processed as php, because the filename ends in .png
.
If I'm an attacker who's aware of PATH_INFO
, next I'll try http://example.com/upload/evil.png/index.php
. If your server is so configured, this will result in PHP being executed (because nginx sees the index.php
at the end) and PHP will walk the path until it finds a component that is a file, not a directory (evil.png
) and attempt to execute it. Then my shell gets executed and I win.
That being said, there's better ways to deal with this by having the NGINX config split the path in advance, so PHP isn't walking the filesystem.
From Neal Poole's excellent blog post on the issue:
# Pass all .php files onto a php-fpm/php-fcgi server.
location ~ \.php$ {
# Zero-day exploit defense.
# http://forum.nginx.org/read.php?2,88845,page=3
# Won't work properly (404 error) if the file is not stored on this server, which is entirely possible with php-fpm/php-fcgi.
# Comment the 'try_files' line out if you set up php-fpm/php-fcgi on another machine. And then cross your fingers that you won't get hacked.
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
include fastcgi_params;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass php;
}