Brute Force Attack is a daily problem for WordPress sites. What’s interesting is that you cannot prevent it from happening. It’s unavoidable. You can only make harder for the attacker to attack your sites.
If we use CMS with login feature to manage our content, we cannot remove/disable the login functionality because we need it to get access to manage the site.
You can use the strongest password, two factor authentication, etc. But it will not stop the attack to your site.
Every single login attempt will cost you server resource. You cannot cache this page to reduce the impact because WordPress need to validate each login attempt.
But, we can try to discourage attacker by blocking IP addresses they use. If you use relatively good hosting, you probably have firewall system installed in your server to log and block attacker. But you can also install security plugin to add another layer of security. Several plugin for WordPress brute force protection:
- Limit Login Attempts : un-maintained plugin, if I’m not mistaken WP Engine auto-activate this plugin for sites hosted there.
- BruteProtect : Use their server to log IP addresses, kinda like Akismet for brute force attack. You need to register to their site to get API key for each of your site. Currently owned by Automattic.
- Login Security Solution : similar with limit login attempt, maintained. And have multi-site support. This is the plugin I’m using.
- And a lot more alternative…
It will reduce their attack, but because they seem to have unlimited number of IP Address, it’s actually (kinda) useless method to try to discourage them.
Quoting from Matt Mullenweg:
Supposedly this botnet has over 90,000 IP addresses, so an IP limiting or login throttling plugin isn’t going to be great (they could try from a different IP a second for 24 hours).
You can also read other sources to understand the scale of the attack:
- WordFench : Large distributed brute force attack underway at 40,000 attacks per minute.
- Sucuri Blog: Understanding Denial of Service and Brute Force Attacks – WordPress, Joomla, Drupal, vBulletin
Every single day in each sites I got hundreds of failed login attempt. Probably tens of thousands if it’s not protected by firewall and security plugins. It happen in every single site. Not even one site is free from brute force attack.
Several days ago, I ask for advice at Theme Hyrid Forum (private forum replies). I got several response. And from their response I create a custom solution for my sites and my clients sites.
I tested it in 10 different sites for 24 hours, the result is amazing. I got almost zero login attempt.
Even though it’s still premature to say that this solution is working. In this post I would like to share the custom solution I build to solve this problem.
It’s very simple. The idea is to disable
wp-login.php and create custom login URL. There’s several plugins to “hide” WordPress login page, but none is suitable for my needs. It’s actually simple to do, but in the same time also complex. The code is not yet available for public, but in this post I would like to walk-through to the concept of this plugin (more like the plugin wire-frame).
I want to polish this plugin so it will be suitable for public use,
it’s going to take a lot of my time to build it. And I’m not sure if anyone interested in using this solution.
So, let me know in the comment if you think you want to use this solution for your site. I’ll make a time to write it and release this plugin to wp.org repository.
Create Custom “Hidden” Login Page.
So I create custom login template accessible by custom URL. For example,
So we need custom URL variable with specific value to get access to the page. So all login purpose only works from this URL.
It’s relatively simple to code:
- Register our custom query var using
query_varsfilter, for example
- Decide the value to access login page, in this example the value is
template_includefilter to load our custom template if we load the url.
- In this template use
wp_login_form()function to generate our login form.
- Create extra nonce to make this form unique. This nonce will be used to white list this form from wp-login.php (read section below).
This is the tricky part. wp-login.php is used as central user authentication. Not only for “login” purpose. WordPress use it for “logout”, “lost password”, “re-authenticate” user if session expired, etc. This is a very busy file.
Even login form created using
wp_login_form() simply submit the data to
wp-login.php will authenticate the request.
We don’t want to change other functionality in wp-login.php we only want to disable it for “login” purpose except if the login request is from our custom (hidden) login page. To do this:
- Hook to
login_initaction to intercept and validate the request.
- Verify our custom form nonce, and If the action is
loginredirect it to our custom login URL.
- If the action is to
login, but the nonce is invalid, redirect it somewhere else (for example your error/404 page).
- Leave other action such as forget password, etc so wp-login.php will work as is.
- Add redirect to logout url using
filter if needed. As default it will redirect to wp-login.php, and because we disable it, user will be redirected to the page you set in step (3). Use it so they can be redirected to other pages/url.
Well, that’s it. In this site I even add the custom login URL in my site footer (so it’s not really a secret URL). and during this time period I got zero login attempt.
So I think we can safely say that by simply creating custom login URL and disable wp-login.php will cripple most of brute force attack in WordPress.
But lets push it further.
Dynamic Login URL for WordPress
Even though the solution to change the login URL is working well, but it’s not an ideal solution for protection.
The secret login URL is using “Security through obscurity” method, which is fine (it’s the same protection we do by not sharing our password publicly). But the better security method is “Security by Design“. The system is designed to be secure. (like using two factor authentication in addition to password method).
I don’t think we can 100% protect login form, because we need it to log-in. but we can design the system better,
Creating hidden/secret login URL (without sharing it to public) can also be a problem. If we forgot our secret URL, we cannot login to our own site. And if we have membership site, the login URL need to be a public because we are not the only one who need to login to the site.
Protect WordPress Login URL for Membership site.
The idea to address this issue is to create a dynamic (temporary) login URL. We can add this link in our site (maybe in navigation or sidebar) so we don’t need to remember our login URL.
This link is generated using wp nonce (valid for around 24 hour, as default), combined with user/visitor IP Address, User Agent, etc as unique identifier for each request. This is needed because wp nonce is designed for logged in user, because this URL is not for logged in user we need other method to identified each session.
So if attacker grab this URL, it will only valid for max 24 hour, and only valid for one IP Address.
They cannot abuse this URL to access login page with multiple IPs. So they need to grab different URL for each of their IPs, and there’s also time frame where they need to grab new URL because the URL already expired.
This is best combined with login protection plugin ( track and banned IPs ) to give even extra layer of security.
Of course If it’s not secure enough, we can extend and fine tune this dynamic URL in the future. If ( for example ) we need it to generate unique URL per hour, etc.
The cons for Dynamic Login URL is that we cannot bookmark it or share it (obviously).
Actually we can use this dynamic URL solution without fixed “secret” url as explained above.
In my test displaying the “secret” login URL for the last 24 hour (in my site footer navigation) do not seem to invite any attack (yet), and displaying this dynamic login URL for public is definitely saver than displaying the fixed secret URL.
To create this dynamic URL (it’s very simple):
- Generate nonce using
- Create our own custom nonce using IP Address, User Agent, etc using
- Add this nonces to the URL.
- If we visit this url, validate the nonces. If valid, display the form,
- if not-valid, display error or redirect them to error page.
Let’s Play Hide and Seek.
In this site, and other sites where I implement this solution. If we try to visit
wp-login.php , I redirect them to error page where I put the link to request login url, like this:
And the target of “Lost Login URL?” link is a page with Dynamic Login URL:
And the result is the same by adding this URL publicly in the site. almost zero failed login attempt (brute force attack) in all sites.
Other possible improvement is to create user name/email input and send the URL via email after verifying the email (maybe that’s too much, it’s like requesting new password every login), or maybe use captcha or other method to show the URL.
Note: there’s several login attempt happen in two site after I display dynamic login URL, but I think the attack is done manually, because it only happen less than 20 attempt in each site (24 hours test).
I still feel uneasy with all the login protection. Even if I can check the log of how many login attempt, I want to know if someone get access to my account (worst case scenario). So I create additional functionality in this plugin to send me email for every successful login.
There’s several plugins with this functionality, but it’s relatively simple plugin, so I wrote a quick code for it:
- Hook to
wp_loginto check successful login.
wp_mail()function to send email with user data and other information you need such as IP Address, etc.
It’s not ideal, since I only create base functionality that works for my needs, but the ideal login email notification plugin would be:
Admin Login Notification:
- Send every successful login to admin email.
- Option to exclude user roles to track. We don’t need to get notification for all user.
- Options to modify admin email notification template.
User Login Notification:
- Send every successful login to the user email (who login).
- User preference option to opt-in/out this feature via profile edit screen.
- Ability for administrator to force enable/disable this feature and enable/disable user preference option.
- Options to modify user email notification template.
The only cons with email notification is that you will get email every time you login 🙂
I think it’s annoying sometimes. But I think it’s worth it.
NOTE: My login notification plugin now available at WordPress.org: f(x) Login Notification.
What do you think about this solution? What plugin you use to secure your WordPress site?
If you have suggestion and/or advice to protect WordPress site, share it in the comment.
Update 19 July 2016:
Since BruteProtect merged into Jetpack, I think it’s best to simply use the plugin (So, for my sites I no longer use this method or experiment further.
But I did create login noification plugin, and it’s available on WordPress.org: f(x) Login Notification.