WordPress harderning with apache

By | December 4, 2015

Installing WordFence #1 #2 gives you protection and great visibility into whats going on. It’s easy to think no problems = no attacks, but after installing WordFence it was immediately obvious how much attach my little old website was under – and not because it’s a popular site (far from it – I’m pretty sure I’m the only one who ever read it when I need to refer back to something I’ve done) but because it’s using WordPress, and once in an attacker comprises the site, they use the host to start spamming people (I’ve seen first hand this week as a customer of mine had their WP site hacked which is what started my renewed interest in WordPress hardening).

Install WP Security Audit Log #1 #2 to give your an Audit Trail as to actions performed by logged in users. This may be surplus to requirements with WordFence being installed but for the moment I want it installed. My opinion may change.

I don’t permit customers to use .htaccess files for performance and security reasons so I set these configuration details in an included .conf file in the servers config directory (I have a directory which stores separate .conf files for each vhost and my main httpd.conf just includes /somedirectory/*.conf). Depending on your setup you might need to add them to your httpd.conf. I’ve also highlighted some lines in read that I’m pretty confident you could just add to your .htaccess file should you wish to do it that way.

Note, I’m using the GeoIP module to filter access to wp-login.php and /wp-admin/ directory by country. I live in Great Britain so it makes sense I’ll only ever administrate my blog from the UK (I’m not going to be doing it when I’m on holiday!!)

<VirtualHost *:80>
 DocumentRoot "/var/www/net/example/www/http"
 ServerName www.example.net
 ServerAlias example.net
 ServerAdmin webmaster@www.example.net

 CustomLog /var/www/net/example/www/access.log combined
 ErrorLog /var/www/net/example/www/error.log

 #
 # Recommended by
 # https://www.ndchost.com/wiki/wordpress/how-to-harden-wordpress-security
 # https://blog.sucuri.net/2015/10/brute-force-amplification-attacks-against-wordpress-xmlrpc.html
 #
 ############################################################################
 # Beginning of WordPress Harderning Block 1
 ############################################################################

 # Disable directory browsing
 Options All -Indexes

 <IfModule mod_headers.c>
 # No ETags, No Pragma
 Header unset Pragma
 Header unset ETag
 # Make sure proxies deliver correct content
 Header append Vary User-Agent env=!dont-vary
 # Ensure proxies deliver compressed content correctly
 Header append Vary Accept-Encoding
 </IfModule>

 <Files wp-config.php>
 order allow,deny
 allow from 127.0.0.1
 deny from all
 </Files>

 <Files xmlrpc.php>
 order allow,deny
 allow from 127.0.0.1
 deny from all
 </Files>

 # This block (which I pinched from WordFence) mitigates risk by
 # preventing the listed extensions from being executed should an 
 # attacker somehow get these file types uploaded
 <Directory /var/www/net/example/www/http/wp-content/uploads/>
 # BEGIN Wordfence code execution protection
 <IfModule mod_php5.c>
 php_flag engine 0
 </IfModule>

 AddHandler cgi-script .php .phtml .php3 .pl .py .jsp .asp .htm .shtml .sh .cgi
 Options -ExecCGI
 # END Wordfence code execution protection
 </directory>

 <IfModule geoip_module>
 GeoIPEnable On
 GeoIPDBFile /usr/local/share/GeoIP/GeoIP.dat

 <Files wp-login.php>
 SetEnvIf GEOIP_COUNTRY_CODE GB AllowCountry
 Require env AllowCountry

 RewriteEngine on
 RewriteCond %{ENV:GEOIP_COUNTRY_CODE} ^!GB$
 RewriteRule ^(.*)$ http://geoipban.spectrumcs.net/?request=%{HTTP_HOST}$1&port=%{SERVER_PORT} [R,L] 
 </Files>

 <Directory /var/www/net/example/www/http/wp-admin>
 SetEnvIf GEOIP_COUNTRY_CODE GB AllowCountry
 Require env AllowCountry

 RewriteEngine on
 RewriteCond %{ENV:GEOIP_COUNTRY_CODE} ^!GB$
 RewriteRule ^(.*)$ http://geoipban.spectrumcs.net/?request=%{HTTP_HOST}$1&port=%{SERVER_PORT} [R,L]
 </Directory>
 </IfModule>
 ############################################################################
 # End of WordPress Harderning Block 2
 ############################################################################


 <Directory /var/www/net/example/www/http>
 Require all granted

 Options FollowSymLinks SymLinksIfOwnerMatch
 # Block the include-only files.
 RewriteEngine On
 RewriteBase /
 RewriteRule ^wp-admin/includes/ - [F,L]
 RewriteRule !^wp-includes/ - [S=3]
 RewriteRule ^wp-includes/[^/]+\.php$ - [F,L]
 RewriteRule ^wp-includes/js/tinymce/langs/.+\.php - [F,L]
 RewriteRule ^wp-includes/theme-compat/ - [F,L]

 # May not be required now I've added the -ExecCGI to the /var/www/net/example/www/http/wp-content/uploads/ above.
 #RewriteRule ^wp-content/uploads/[^/]+\.php$ - [F,L]
 #RewriteRule ^wp-content/uploads/[^/]+/[^/]+\.php$ - [F,L]
 #RewriteRule ^wp-content/uploads/[^/]+/[^/]+/[^/]+\.php$ - [F,L]


 # BEGIN WordPress
 <IfModule mod_rewrite.c>
 RewriteEngine On
 RewriteBase /
 RewriteCond %{REQUEST_FILENAME} !-f
 RewriteCond %{REQUEST_FILENAME} !-d
 RewriteRule . /index.php [L]
 </IfModule>
 # END WordPress
 </Directory>

 RewriteEngine On
 RewriteCond %{HTTP_HOST} !^www.example.net [nocase]
 RewriteRule ^(.*)$ http://www.example.net$1 [last,redirect=301]
</VirtualHost>