Skip to content

Securing WordPress

3064firefoxchromeieRecently, I received a couple of “Password Reset” request emails from my blog. I surmised that someone had tried to log into my WordPress administrative account, was unsuccessfully, and had resorted to using the “Lost your password?” link on the login page. Disconcertingly, the email indicated that my admin username was being used. I checked my blog and thankfully, it looked like nothing was wrong.

It was time to make my WordPress installation more secured.

Disable Password Reset

I decided to remove the “Lost your password?” link on the WordPress login page. I doubted that I would forget my admin password and even if I did, I could always go directly to the MySQL database to change it. Because I didn’t need to support more than one user (my admin user), I decided to use the simpler manual method from How to Remove the Password Reset / Change option from WordPress, instead of the Plainview Protect Passwords plugin.

Here are the steps to globally disable the password reset option:

  1. Create a file named “disable-password-reset.php” with the following content:
    <?php
    /*
     * Plugin Name: Disable Password Reset
     * Description: Disable password reset functionality. Only users with administrator role will be able to change passwords from inside admin area.
     * Version: 1.0
     * Author: WPBeginner
     * Author URI: http://wpbeginner.com
     */

     
    class Password_Reset_Removed
    {
     
      function __construct()
      {
        add_filter( 'show_password_fields', array( $this, 'disable' ) );
        add_filter( 'allow_password_reset', array( $this, 'disable' ) );
        add_filter( 'gettext',              array( $this, 'remove' ) );
      }
     
      function disable()
      {
        if ( is_admin() ) {
          $userdata = wp_get_current_user();
          $user = new WP_User($userdata->ID);
          if ( !empty( $user->roles ) && is_array( $user->roles ) && $user->roles[0] == 'administrator' )
            return true;
        }
        return false;
      }
     
      function remove($text)
      {
        return str_replace( array('Lost your password?', 'Lost your password'), '', trim($text, '?') );
      }
    }
     
    $pass_reset_removed = new Password_Reset_Removed();
    ?>
  2. Upload this file to your server’s WordPress plugins directory (“/var/www/wordpress/wp-content/plugins”).
  3. Log into WordPress, go to Plugins, and activate the “Disable Password Reset” plugin. The password reset option will now be disabled for all users, including administrators.

As a precaution, I also changed the admin username because it had been compromised. This was accomplished by creating a new admin user in WordPress, logging into the new user, and then deleting the old user.

Force Login to Require Secure HTTPS

When I configured my VPS (Virtual Private Server), I allowed the WordPress login page and administrative areas to be accessible by unsecured HTTP. So anyone sniffing data packets on the Internet could see, in plain text, the info my browser was sending to my server. This may explain how my admin username was known to whomever was attempting to access my WordPress site.

To prevent the above, I decided to force the WordPress login and administration pages to require secure HTTPS access. I found the simplest method at WordPress SSL Settings and How to Resolve Mixed Content Warnings, which was to enable the “FORCE_SSL_ADMIN” WordPress option.

Edit the WordPress configuration file (“/var/www/wordpress/wp-config.php”) and add the text below before the “/* That’s all, stop editing! Happy blogging. */” statement at the bottom of the file:

/**
 * Secure (force HTTPS) entire wp-admin area
 * Note: Includes FORCE_SSL_LOGIN which secures login.php script
 */

define('FORCE_SSL_ADMIN', true);

/* That's all, stop editing! Happy blogging. */

After the change above, even if I were to access the login page using unsecured HTTP, it would automatically redirect me to the secure HTTPS login page.

Your Connection Is Not Fully Secure

Normally, when accessing a website using secure HTTPS, the browser would display a padlock next to the URL to indicate that the connection is secure. (Chrome shows a yellow padlock before the URL. Internet Explorer shows a grey padlock after the URL.) However, when I accessed my WordPress site using secure HTTPS, the padlock was not shown. Under Chrome, when I clicked on the info icon next to the URL, a message “Your connection to this site is not fully secure” was displayed.

I tested my site using Why No Padlock? and it found two issues:

  1. Server supports SSLv3, may be vulnerable to POODLE attack. It is suggested to disable the SSLv3 protocol.
  2. Number of insecure items: 15. (A.k.a. the mixed content issue. All 15 items were unsecured HTTP image links.)

The first issue can be resolved by disabling the SSL v3 protocol:

  1. Edit the Nginx server block file (“/etc/nginx/sites-available/default”). Locate and add/modify the “ssl_protocols” directive like below (the first line is commented out):
    #       ssl_protocols SSLv3 TLSv1;
            ssl_protocols TLSv1.2 TLSv1.1 TLSv1;
  2. Restart the Nginx service so the changes above will take effect:
    sudo service nginx restart

Note: When I restarted Nginx, I saw warnings in the Nginx error log (“/var/log/nginx/error.log”) that looked like the following:

2017/09/02 01:15:24 [warn] 20631#0: duplicate value "TLSv1.2" in /etc/nginx/sites-enabled/mydomain:128
2017/09/02 01:15:24 [warn] 20631#0: duplicate value "TLSv1.1" in /etc/nginx/sites-enabled/mydomain:128
2017/09/02 01:15:24 [warn] 20631#0: duplicate value "TLSv1" in /etc/nginx/sites-enabled/mydomain:128

I found that it was caused by having two “ssl_protocols” directives in the Nginx server block file. Once I removed the duplicate directive, the warnings disappeared.

If you have more than one domain (resulting in more than one Nginx server block file), you can add the “ssl_protocols” directive to the Nginx configuration file (“/etc/nginx/nginx.conf”), instead of to all the server block files. Directives in the Nginx configuration file will apply to all server block files.

The second issue, “insecure items” or mixed content, was caused by unsecured image link URLs in each blog post’s content. Fixing it would require editing all image links to use the secured URL instead. I plan to replace the unsecured image link URLs with secured URLs over time. Eventually, I hope to see the padlock on secured HTTPS access to my WordPress site. (By default, WordPress inserts an image link using an unsecured URL. So you will need to edit the pasted image link manually to use the secured URL.)

Note: I was concerned that browsers would not cache images downloaded by secure HTTPS. However, after some research, I found out that modern browsers (as of 2010) will cache HTTPS content by default.

WordPress Debug Mode

If you haven’t already done so, you might consider temporarily enabling the WordPress debug mode to see if there are issues with the theme or plugins. Setting debug mode will return a wealth of log messages (all errors, notices, and warnings) that might point out potential issues with your WordPress installation.

To set debug mode, edit the WordPress configuration file (“/var/www/wordpress/wp-config.php”) and add the following directives before the “/* That’s all, stop editing! Happy blogging. */” statement:

// Enable WP_DEBUG mode
define( 'WP_DEBUG', true );

// Enable Debug logging to the /wp-content/debug.log file
define( 'WP_DEBUG_LOG', true );

// Disable display of errors and warnings inside the page HTML
define( 'WP_DEBUG_DISPLAY', false );
@ini_set( 'display_errors', 0 );

// Use dev versions of core JS and CSS files (only if modifying them)
define( 'SCRIPT_DEBUG', true );

/* That's all, stop editing! Happy blogging. */

Once you are satisfied, don’t forget to disable the debug mode because it may adversely affect the performance of your WordPress site.

// Enable WP_DEBUG mode
define( 'WP_DEBUG', false );

I found only notices and PHP strict warnings concerning some plugins, but thankfully, no errors.

Leave a Reply

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