'Nginx disallowing execution of PHP in uploads directory with Magento

I'm having difficulties getting nginx to stop execution of PHP in an uploads directory on a magento install. I've tried many combinations of directives that should've sent a 503 or similar when *.php is matched in that directory, but still I'm able execute PHP in there. Of-course the code solution is to prevent .php files from being uploaded but I don't understand how to prevent the execution from an nginx perspective.

map $http_x_ssl_offloaded $fastcgi_https {
  default off;
  on      on;
}


server {
    listen       80;
    server_name  store.xxxx.com;
    root         /var/www/store.xxxx.com;

    #charset koi8-r;


    #access_log  /var/log/nginx/store.xxxx.com-access.log  main;
    access_log  /var/log/nginx/store.xxxx.com-access.log;
error_log   /var/log/nginx/store.xxxx.com-error.log;


gzip on;
gzip_disable msie6;
gzip_static on;
gzip_comp_level 9;
gzip_proxied any;
gzip_types text/plain text/css application/x-javascript text/xml application/xml application/xml+rss text/javascript;


 location / {
    index index.html index.php;
    try_files $uri $uri @handler;
    expires 30d; ## Assume all files are cachable
}

## Location for media to prevent execution of php
location ^~ /media/              {}

## These locations would be hidden by .htaccess normally
location ^~ /app/                { deny all; }
location ^~ /includes/           { deny all; }
location ^~ /lib/                { deny all; }
location ^~ /media/downloadable/ { deny all; }
location ^~ /pkginfo/            { deny all; }
location ^~ /report/config.xml   { deny all; }
location ^~ /var/                { deny all; }

location /var/export/ { ## Allow admins only to view export folder
    auth_basic           "Restricted"; ## Message shown in login window
    auth_basic_user_file htpasswd; ## See /etc/nginx/htpassword
    autoindex            on;
}

location  /. { ## Disable .htaccess and other hidden files
    return 404;
}

location @handler { ## Magento uses a common front handler
    rewrite / /index.php;
}

location ~ .php/ { ## Forward paths like /js/index.php/x.js to relevant handler
    rewrite ^(.*.php)/ $1 last;
}

# Pass all PHP scripts to the PHP-FPM
location ~ [^/]\.php(/|$) {

    fastcgi_split_path_info ^(.+?\.php)(/.*)$;
    if (!-f $document_root$fastcgi_script_name) {
            return 404;
    }

    expires        off; ## Do not cache dynamic content
    fastcgi_pass   127.0.0.1:9000;
    include        fastcgi_params;

    fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
    fastcgi_param  MAGE_RUN_CODE en_us;
    fastcgi_param  MAGE_RUN_TYPE store;
    fastcgi_param  SSL_OFFLOADED $fastcgi_https;
    fastcgi_param  HTTPS $fastcgi_https;

    fastcgi_read_timeout 6000;
}

location /api {
    rewrite ^/api/rest /api.php?type=rest last;
    rewrite ^/api/v2_soap /api.php?type=v2_soap last;
    rewrite ^/api/soap /api.php?type=soap last;
}

location /nginx_status {
        stub_status on;
        access_log   off;
        allow 127.0.0.1;
        deny all;
}

# deny running scripts inside writable directories
location ~* /(images|cache|media|logs|tmp)/.*\.(php|pl|py|jsp|asp|sh|cgi)$ {
        return 403;
        error_page 403 /403_error.html;
}

# caching of files
location ~* \.(ico|pdf|flv)$ {
        expires 1y;
}

location ~* \.(js|css|png|jpg|jpeg|gif|swf|xml|txt)$ {
        expires 14d;
}

# redirect server error pages to the static page /40x.html
#
error_page  404              /404.html;
location = /40x.html {
}

# redirect server error pages to the static page /50x.html
#
error_page   500 502 503 504  /50x.html;
location = /50x.html {
}

# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
location ~ /\.ht {
    deny  all;
}
}


Solution 1:[1]

The official docs say:

...regular expressions are checked in the order of their appearance in the configuration file. The search of regular expressions terminates on the first match, and the corresponding configuration is used.

Therefore your location for denying scripts should come before the location for executing scripts.


I am less certain here but I think the reason this doesn't work:

## Location for media to prevent execution of php
location ^~ /media/              {}

...is because it has no immediate effect and the compiler optimises it out completely.

Solution 2:[2]

Try to add to the line:

location ~ [^/]\.php(/|$) {

the following directive (change "your_directory" with the folder you need to forbid php file uploads for):

location ~* /your_directory/.*\.php$ {
return 503;
}

Solution 3:[3]

Try

location /media/ {

   ......

   # Banned locations
    location ~* (\.php$|\.phtml$|\.htaccess$|\.git) {
        deny all;
    }
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 clockworkgeek
Solution 2 Mageworx
Solution 3 MagePal Extensions