Improving WordPress Login Security – Revisted

In a previous article, I spoke of a method for restricting access to the WordPress login screen by directing requests for “wp-login.php” away to a standard WordPress 404 page.

Server Ethernet Ports
https://www.flickr.com/photos/jemimus/8469760647

Recently I came across a situation with one site that I manage, where an unrelated change meant that this was no longer working. Interestingly, on some sites I manage it still works, and on others it does not – despite the underlying configuration of the web host being no different.

I believe I do know why it stopped working on some sites – but I choose not to explain the reason at this time for security purposes.

To get the “non-working” sites to work again, the included configuration needed to replace the configuration construct with the following, using the “Require” directive instead:

<Location />        
  ErrorDocument 403 /idontthinksotim
</Location>
<Files .htaccess>
  Require all denied
</Files>
<Files wp-login.php>
  Require all denied
  Require ip aa.bb.cc.dd
</Files>


Refer to the previous article for complete understanding, but the “Require all denied” directive here is equivalent to the combination of “order deny,allow” and “deny from all” directives in the previous example. It basically says “by default, deny everyone from the file.”

Each allowed IP is then listed in sequence below that – in this example “Require ip aa.bb.cc.dd”. This of course will need to be the IP address you wish to allow access from, and as before, can also use CIDR notation to allow entire ranges of IP addresses.

Other than the use of the “Require” directives, the concept of this article remains the same as the previous, and once again, don’t assume your website is 100% protected if you do this. Hackers are clever people, and if they are determined they will find a way.

Basically, each version could and should work – but if you find you’ve tried one, and it doesn’t work – try the other!

Improving WordPress Login Security

WordPress is the most common CMS used by websites, recently topping 43% market share of all sites currently on the internet.

With such a significant presence, it is also the largest target for website hackers, and given that it is open-source, good and bad actors are always examining the code for vulnerabilities.

There are plenty of things you can do to tighten defences – the Wordfence plugin is an excellent start, that I highly recommend.

https://flickr.com/photos/136770128@N07/40492737110

Another thing you can do is restrict access to the “wp-login.php” script, based on IP address – however, note that this solution will only work if you have a fixed and known IP address from which you will be logging in to your site.

If you move around, you’re probably locking yourself out of your own website console unless you’re at the IP address we’ll use in the example below. The example below is specific to Apache web servers, but the same principle can be applied to other configurations.

Let’s say my IP address is “aa.bb.cc.dd”. Put the following in the virtual host configuration for your WordPress website, and you can now only log in to your website from that IP address. Your site is still completely visible to the internet, but even if someone has your username and password, that login will be denied – they won’t even get to the login page.

<Location />
  ErrorDocument 403 /idontthinksotim
</Location>
<Files wp-login.php>
  order deny,allow
  deny from all
  allow from aa.bb.cc.dd
</Files>

The “order deny,allow” command tells Apache that it should follow any “deny” command access to “wp-login.php” first. The “deny from all” command is the only example of that that we need here. The “allow from aa.bb.cc.dd” command allows only the specifically listed IP address to get to “wp-login.php”.

You can of course add multiple “allow from” commands, and if you understand CIDR notation, you can use that to specify ranges of IP addresses you might want to allow with a single entry.

The above code means every other IP address on the internet is denied access to “wp-login.php”, and causes a “403” error to be thrown. To make things nice and neat and pretty, I have redirected “403” errors to a URL that does not exist – so that visitors are greeted with a proper “404” page from your WordPress site, rather than the standard “403” Apache error screen.

One final note – there are lots of other ways for bad actors to compromise a website – this is just another potential tool in your bag of tricks to keep them out. Don’t assume your website is 100% protected if you do this. Hackers are clever people, and if they are determined they will find a way.

Extracting Wordfence Attacker Data

Wordfence is a web application firewall (WAF) designed for WordPress – (the most common website platform on the internet today) – which I have used and trusted for a long time.

Wordfence helps identify attacks and vulnerabilities on your WordPress sites and takes appropriate action to mitigate what it finds. It comes in two forms – a paid and unpaid version, where the paid version gives more rapid updates to its core list of security vulnerabilities and known bad actors, as well as direct support if your attacker does manage to breach your site.

https://www.flickr.com/photos/140988606@N08/36883845116

I highly recommend it – it can be a little tricky to configure but is well worth the effort.

When it finds something hitting your site, the most common thing it does is to block the IP address of the attacker, carte blanche.

As I operate a series of WordPress sites – (in both my professional and personal spheres) – it would be nice to be able to extract the list of all of the IP addresses Wordfence has blocked, to ingest that list into other security systems you might have so they can be reused to implement security policy on other internet facing assets.

I do exactly this to block these identified bad IP addresses for accessing any service on any server I am responsible for.

On the surface, the data Wordfence stores in your WordPress database isn’t easily identifiable as an IP address, so it needs to be translated, for which I use the following SQL:

SELECT `raw_data`.`ipaddress` AS `ipaddress`,`raw_data`.`count` AS `count` FROM (SELECT REPLACE(CONVERT(INET6_NTOA(`wordpress_database_name`.`wp_wfBlockedIPLog`.`IP`) USING utf8mb4),'::ffff:','') AS `ipaddress`,SUM(`wordpress_database_name`.`wp_wfBlockedIPLog`.`blockCount`) AS `count` FROM `wordpress_database_name`.`wp_wfBlockedIPLog` GROUP BY `ipaddress`) `raw_data` ORDER BY `raw_data`.`ipaddress`;

When using the above, change ‘wordpress_database_name’ to the actual name of the database your WordPress installation is using.

Also, I’ve noticed that sometimes the Wordfence table ‘wp_wfBlockedIPLog’ can have different capitalisation, and can appear to be ‘wp_wfblockediplog’ – just look for the table, and change the SQL above to suit the name as it stands in your database.

This query spits out a list of blocked IP addresses, and a count of how many times it has been blocked. I usually create a database view using this query so that it is queriable in the same way as a table, making it much easier to work with.

Once you have that, you can use the data to spread the knowledge of bad IP addresses across your infrastructure, and use the intelligence Wordfence provides to help secure that infrastructure.

Tesla Key Fob Hack – Are We Too Clever?

The recently revealed vulnerability enabling hackers to trivially duplicate Tesla Model S key fobs, in my mind prompts an interesting technology question.

The Hack in a Nutshell

This does not apply to all Model S vehicles, but in simple terms, using a few hundred dollars of off-the-shelf radio and computer hardware, malicious actors can intercept transmissions from your key fob when nearby.

Using the intercepted data and about two seconds of computational power, they are able to duplicate your key fob.

This allows them to open your Tesla Model S, start your Tesla Model S, and drive your Tesla Model S away.

Noting that the cryptographic keys in use are only 40-bit keys, quoting from the Wired article:

The researchers found that once they gained two codes from any given key fob, they could simply try every possible cryptographic key until they found the one that unlocked the car. They then computed all the possible keys for any combination of code pairs to create a massive, 6-terabyte table of pre-computed keys. With that table and those two codes, the hackers say they can look up the correct cryptographic key to spoof any key fob in just 1.6 seconds.

The High-Tech Solution

To solve this vulnerability, Tesla are recommending a firmware update to the security systems in the Model S.

After unlocking the car and disabling the immobiliser system with the key fob, drivers would now need to enter a PIN on the console of the car before they can start it.

This provides rudimentary two-factor authentication, and is probably a reasonable solution to the problem, albeit lowering convenience for the owner.

Until the hackers figure out how to bypass the PIN code – and if the carrot is dangled, they will try.

Hackers are typically highly intelligent people who crave the challenge.

So, what else could we do?

The Lower-Tech Solution

As humans, how did we cope with unlocking our cars and starting them up before remote key fobs?

We coped, and we coped very well.

People walked up to their cars, and put the key in the door. They got inside and put the key in the ignition, and were on their way.

Why aren’t we still doing this?

Key systems without radio transmitters can still contain security codes, which could be read by the car when the key comes into physical contact with it.

All without broadcasting the security codes for hackers to scan and potentially use against you.

It would be harder to steal your car – and would our lives be that much more difficult if we stepped back to something like this?

Sometimes simple proven ideas are far better for us than fancy new ideas that haven’t been completely thought through.

HTTPS Is The New Black

With the advent of modern web browsers flagging all non-HTTPS web traffic as “not secure”, I have a few tips on what to do if you are running a website. Google announced the change some months ago, and Mozilla is following suit with their key products also.

If you are running a website, and you don’t run HTTPS and don’t enforce HTTPS by default, this affects you.

You need to fix it.

Modern web browsers typically upgrade themselves automatically. As such, all of your users will soon receive warnings that show your HTTP website as being “not secure”. They are going to complain.

What are HTTP and HTTPS?

HTTPS is a secure, encrypted version of the original HTTP protocol, instigated by Tim Berners-Lee. When he started developing the world wide web in the early 1990s, the security of the transmission of the data wasn’t considered too important.

Not many people were on the internet, and most of the people who were were considered “trustworthy”.

This has changed – and the switch to HTTPS – which basically takes HTTP, and wraps it up in an encrypted stream of data designed to prevent snooping of the traffic as it crosses the internet is absolutely the “new black” of the internet.

One of the biggest barriers to HTTPS uptake has been the cost of obtaining SSL certificates. They can and do cost several hundreds of dollars for certificates that expire – (typically) – every two years. Such costs are prohibitive for many people, particularly bloggers and small businesses who can’t justify that cost.

The solution?

Along came Let’s Encrypt, an issuer of free SSL/TLS certificates and sponsored by many industry heavyweights.

The arrival of Let’s Encrypt has sparked a massive surge in the uptake of HTTPS by websites, and now more than half of the webpages on the internet are available using HTTPS. This is a huge win for internet users, keeping their communications encrypted and secure when browsing sites with HTTPS switched on.

And because their certificates are free, the barriers holding many people up from making the switch are mostly gone. They do expire every 90 days, but most web hosting companies have embraced them, and have automated mechanisms for the renewal of the certificates without human intervention.

What should I do?
  • If you are hosting and managing your website on your own servers, you probably have the smarts to use Let’s Encrypt to set up the certificates and make the necessary changes to the configuration of your web server yourself. Follow the information in their documentation.
  • If you are hosting your website on the servers of the company you are working for, contact your local systems administrators and seek their assistance.
  • If you are hosting your website on the servers of a web hosting company, contact their service desk team and seek their assistance.

An Important Mistake Not To Make

I recently got into an (somewhat heated) online discussion about the right and wrong ways to implement HTTPS. A common mistake I have seen is where the HTTP content of the website and the secured HTTPS content is served from the same document root.

This is bad.

Even if you move to HTTPS, and your content is still available via HTTP, people can still be directed to your site via HTTP. Old links to your site from someone else’s site can send your visitor via HTTP. This leaves them using HTTP for their entire visit.

Ensure that you ONLY serve your content via HTTPS. Point the “HTTP version” of your website to a different document root. From that document root, redirect all HTTP requests to the HTTPS document root.

Here’s how I do it – (note that this is for an Apache web server with PHP):

Firstly, in the exclusive HTTP document root, place an “.htaccess” file with the following content:

RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php?q=$1 [L,QSA]

This serves to send every single request, regardless of the full URL to the file “index.php”. This file should contain the following (inside normal PHP start and finish tags):

@header("Location: https://michaelwyres.com.au".$_SERVER['REQUEST_URI']."",TRUE,301);

This will ensure that all requests to “http://michaelwyres.com.au/whatever-url/” are picked up and sent to “https://michaelwyres.com.au/whatever-url/”, including pages that do not exist.

In this way, if inbound links are still listing HTTP, or your visitor explicitly requests HTTP, it will be trapped and dumped to the HTTPS version of your site. It becomes impossible for people to browse your content using HTTP.