Secure wp-content/uploads in Linux Apache and Windows Server IIS
It’s recommended to disallow access to and execution of PHP files in wp-content/uploads folder. Preferably and without the use of a security plugin. Blocking access to PHP files in WordPress wp-content/uploads folder is easily achieved with a
.htaccess file on Linux Apache, or
web.config accesssPolicy in Windows Server IIS, and here is how.
As said, it is recommended to deny PHP execution in folders like WordPress wp-content/uploads. A lot of malware is uploaded to that folder and used as and entry point. Additional malware and PHP backdoors are uploaded from there. So disable PHP execution in wp-content/uploads! I’ve written about securing WordPress uploads folder before.
The easiest method in IIS to disable PHP execution in a folder is to create a web.config file with an accessPolicy for handlers. Such an accessPolicy tells IIS what a PHP hander is allowed to do: execute, read, or read/write. Using this technique you disable the execution of PHP completely for that particular folder.
Protip: you’ll find more information about IIS Handlers in IIS’ documentation.
In IIS Manager:
- In IIS Manager, click through to your web site
- double click “Handler Mappings“
- search for PHP and double click
- click Request Restrictions
- click the tab Access
- Set Script to Read
Did you know you can use Windows Server File Server Resource Manager File Screens to block the upload of vulnerable WordPress plugins? Read all about it in Deny vulnerable WordPress plugins using Windows Server File Server Resource Manager’s File Screens.
Using a web.config file directly.
Copy and paste the following XML into a new file and save it as web.config:
<?xml version="1.0" encoding="UTF-8"?> <configuration> <system.webServer> <!-- deny PHP execution per IIS accessPolicy --> <handlers accessPolicy="Read"/> </system.webServer> </configuration>
In PowerShell (and
Use the following PowerShell and appcmd.exe commands to configure an accessPolicy for handlers (where example.com is your website):
# PowerShell, WebAdministration Set-WebConfiguration "/system.webServer/handlers/@accessPolicy" -value "Read" -PSPath "MACHINE/WEBROOT/APPHOST/example.com/wp-content/uploads" # AppCmd.exe appcmd.exe set config "example.com/wp-content/uploads" /section:handlers /accessPolicy:Read
Caveat: by setting an accessPolicy to “Read”, you basically disable all configured handlers, not just PHP.
There are several methods to disable PHP execution in Apache using a .htaccess file. I’ll mention them here in short:
SetHandler default-handler to send the file using the
default_handler(), which is the handler used by default to handle static content. It just renders the PHP as text.
wp-content/uploads/.htaccess file, add:
# use <Files *> if appropriate <Files *.php> SetHandler default-handler </Files>
Mod_authz_core .htaccess access control: you can deny access to PHP files using Apache access control in mod_authz_core module. It’ll send a 403 response for requests to *.php files. Add to your .htaccess file:
<Files *.php> # Apache 2.2 <IfModule !mod_authz_core.c> Order Deny,Allow Deny from all </IfModule> # Apache 2.4.6+ <IfModule mod_authz_core.c> Require all denied </IfModule> </Files>
Please note the condition if the module mod_authz_core.c is available. This is important as explained in the above linked article.
Mod_Rewrite: rewrite and forbid requests to *.php files. It’s easy to use mod_rewrite to deny access to *.php files by rewriting those requests and sending a F|forbidden flag:
RewriteEngine On RewriteRule ^.*.php$ - [F,L]
Why did I start this article with
Preferably without the use of a security plugin
Because most security plugins are easy to defeat, especially once you have access to the file system the website resides on (e.g WordPress admin, FTP access, and so on). The post Defeating WordPress Security Plugins (Revisited) by @TheXC3LL has some nice red teaming thoughts about this.
My name is Jan. I am not a hacker, coder, developer or guru. I am merely a systems administrator, doing my daily thing at Vevida.
If you feel a post has helped solve your problem, or has saved you time, please consider making a donation. You can transfer a direct donation through Paypal or via bank wire-transfer IBAN: NL31 ABNA 0432217258 (Jan Reilink). Thanks!