15 Ways to Increase Your WordPress’ Security


1. Don’t Display Error Messages on Failed Login

WordPress’ admin screen displays “ERROR: Invalid username.” if you enter an invalid username. But if you enter a valid username and an incorrect password, it’ll say “ERROR: Incorrect password.” This basic security flaw tells intruders which usernames to target. I don’t understand why the WordPress team designed it this way.

You can disable the error message by putting this in your theme’s functions.php:

add_filter('login_errors',create_function('$a', "return null;"));

2. Publish Posts as a Non-Admin User

Create a WordPress user that has admin privileges and publish all posts as other users that are editor level and below. This makes it harder for intruders to guess which usernames have admin access.

3. Create A Plug-In To Protect Your Blog From Malicious URL Requests

Intruders often try malicious queries to probe website vulnerabilities. Put the following code in a text file and save it as blockbadqueries.php. Upload it to wp-content/plugins and activate it as you would any other plug-in.

Block Bad Querieslink
Plugin Name: Block Bad Queries
Plugin URI: http://perishablepress.com/press/2009/12/22/protect-wordpress-against-malicious-url-requests/
Description: Protect WordPress Against Malicious URL Requests
Author URI: http://perishablepress.com/
Author: Perishable Press
Version: 1.0

global $user_ID;

if ($user_ID) {
    if (!current_user_can('level_10')) {
        if (strlen($_SERVER['REQUEST_URI']) > 255 ||
            strpos($_SERVER['REQUEST_URI'], "eval(") ||
            strpos($_SERVER['REQUEST_URI'], "CONCAT") ||
            strpos($_SERVER['REQUEST_URI'], "UNION+SELECT") ||
            strpos($_SERVER['REQUEST_URI'], "base64")) {
                @header("HTTP/1.1 414 Request-URI Too Long");
                @header("Status: 414 Request-URI Too Long");
                @header("Connection: Close");

4. Remove WordPress Version Number

WordPress automatically displays the version you are using in the head of your blog files. Intruders use this information to target known security holes, especially if you’re running older versions. Put this in your theme’s functions.php:

remove_action('wp_head', 'wp_generator');

5. Don’t Use Default “Admin” Username

Before WordPress 3.0, the default admin username was “Admin.” Can you spell “fail”? If you’re using an older version, run this SQL query:

UPDATE wp_users SET user_login = 'Your New Username' WHERE user_login = 'Admin';

6. Disable Directory Listing

By default, I disable directory listings. Put this either in your root .htaccess or Apache’s site config:

Options -Indexes

7. Separate Database User for WordPress Database

Create a separate database user just for WordPress and restrict its privileges to the relevant WordPress tables. If an intruder successfully cracks one WordPress installation, (s)he won’t take down your other databases.

8. Change Default WordPress Table Prefixes

Many published WordPress-specific SQL-injection attacks make the assumption that the table_prefix is “wp_”, the default. Changing this can block some SQL injection attacks.

9. Secure wp-admin with Apache

Password protect the admin area. Set AllowOverride All in your Apache config and put this in wp-admin’s .htaccess:

# Secure wp-admin folder
AuthUserFile /var/www/apache/passwords
AuthType basic
AuthName "Some message here"
require user [user]

# This is the whitelisting of the ajax handler 

Order allow,deny
Allow from all
Satisfy any

10. Secure wp-config

Deny web access to the wp-config folder by putting this .htaccess in your document root:

# prevent others from accessing this file

order allow,deny
deny from all

# secure wp-config

order allow,deny
deny from all

11. Secure wp-includes

This goes in the same .htaccess file as above:

# secure the wp-includes directory
RewriteRule ^wordpress/wp-admin/includes/ - [F,L]
RewriteRule !^wordpress/wp-includes/ - [S=3]
RewriteRule ^wordpress/wp-includes/[^/] .php$ - [F,L]
RewriteRule ^wordpress/wp-includes/js/tinymce/langs/. .php - [F,L]
RewriteRule ^wordpress/wp-includes/theme-compat/ - [F,L]

12. Protect against script injections

For the same .htaccess file as above:

# protect against script injections
Options  FollowSymLinks
RewriteEngine On
RewriteCond %{QUERY_STRING} () [NC,OR]
RewriteCond %{QUERY_STRING} GLOBALS(=|[|%[0-9A-Z]{0,2}) [OR]
RewriteCond %{QUERY_STRING} _REQUEST(=|[|%[0-9A-Z]{0,2})
RewriteRule ^(.*)$ index.php [F,L]

13. Blacklist IP Addresses

If you notice certain IP addresses spamming you, you can instruct Apache to blacklist their requests by putting this in a .htaccess file in the root of your WordPress installation:

order allow,deny
allow from all
deny from [IP address 1]
deny from [IP address 2]

14. Database Backup

Make sure you use the WP-DBManager plugin to schedule regular backups and email the backup files to yourself, in case crackers break in and wipe out your database.

15. Prevent Hotlinking

This isn’t a security measure, but if you don’t want others hotlinking to your images, put this in .htaccess:

# deters content scrapers from hotlinking
RewriteEngine On
# modify next line accordingly
RewriteCond %{HTTP_REFERER} !^http://(. .)?davidxia.com [NC]
RewriteCond %{HTTP_REFERER} !^$
#Replace /images/nohotlink.jpg with your "don't hotlink" image url
RewriteRule .*.(jpe?g|gif|bmp|png)$ /images/nohotlink.jpg [L]

Of course, this won’t stop them from simply taking your image manually, but at least now you won’t have to worry about them wasting your bandwidth.