Improve WordPress Security: Limit Login Attempts Without a Plugin

Estimated read time 6 min read

Security is essential for every WordPress site owner. While the platform provides many built-in security features, brute force attacks remain a persistent threat. These attacks involve multiple login attempts by hackers trying to guess passwords. This guide will show you a free, simple yet powerful solution that doesn’t require any plugins. It limits login attempts and locks out potential threats, helping keep your WordPress site safe.

Why Limit Login Attempts?

It is common that you find a website where you try to get in but then after 5 (indicative number) attempts you are locked out. It’s frustrating, right? Every website owner should limit login attempts. Setting a maximum number for login attempts will act as good brick wall against brute force attacks. This means it is virtually impossible for some bot or would-be hacker to guess your password and break into your site. It is not just increasing the security of your website; it also provides you with a healthy peace of mind in the knowledge that you are doing something to protect your online property from unwanted guests.

This solution will:

  • Track login attempts by IP address.
  • Lock out users after three failed login attempts.
  • Gradually increase the lockout period after repeated failed attempts.
  • Reset login attempt counts after a successful login.

Let’s break down the code that accomplishes this.

Step 1: Tracking and Blocking Failed Login Attempts

We want to handle monitoring on every user’s attempt to log in and count his failures.

function check_attempted_login($user, $username, $password) {
    $ip_address = sanitize_text_field( $_SERVER['REMOTE_ADDR'] );
    $transient_key = 'attempted_login_' . $ip_address;

    // Retrieve attempt data
    $data = get_transient($transient_key);

    // If the user has exceeded the allowed number of attempts
    if ($data && $data['tried'] >= 3) {
        $until = get_option('_transient_timeout_' . $transient_key);
        $time_remaining = $until - time();

        if ($time_remaining > 0) {
            return new WP_Error(
                'too_many_tried',
                sprintf( __( '<strong>ERROR</strong>: You have reached the authentication limit. Please try again in %s.', 'simplelimitedlogin' ), time_to_go($until) )
            );
        }
    }

    return $user;
}
add_filter('authenticate', 'check_attempted_login', 30, 3);

The particular function checks the number of attempted logins. Once the failed attempts, based on the user’s IP address, reach three or more, he will be restricted from further accessing the account within temporary terms, where an error message will appear before him, saying that he needs to wait a while before retrying it.

Step 2: Handling Failed Logins

Next, we increase the failed attempt count each time a login fails.

function login_failed($username) {
    $ip_address = sanitize_text_field( $_SERVER['REMOTE_ADDR'] );
    $transient_key = 'attempted_login_' . $ip_address;
    $lockout_counter_key = 'lockout_counter_' . $ip_address;

    $data = get_transient($transient_key);
    $lockout_count = get_transient($lockout_counter_key);

    if ($data) {
        $data['tried']++;
    } else {
        $data = array('tried' => 1);
    }

    if ($data['tried'] > 3) return;

    if (!$lockout_count)  $lockout_count = 0;

    if ($data['tried'] == 3) {
        $lockout_count++;
        set_transient($lockout_counter_key, $lockout_count, 86400);
    }

    // Adjust lockout duration based on number of previous lockouts
    $transient_duration = ($lockout_count >= 3) ? 86400 : 1200;
    set_transient($transient_key, $data, $transient_duration);
}
add_action('wp_login_failed', 'login_failed', 10, 1);

In this function, each failure is tracked via WordPress transients-temporary options within the database. After three failures, the user becomes locked out. Each additional failed login further extends the block duration.

Step 3: Resetting on Successful Login

When a user logs in successfully, lockout data should be cleaned so that even after successful authentication, the user is not blocked.

function login_success($user) {
    $ip_address = sanitize_text_field( $_SERVER['REMOTE_ADDR'] );
    $transient_key = 'attempted_login_' . $ip_address;
    $lockout_counter_key = 'lockout_counter_' . $ip_address;

    // Clear lockout data on successful login
    delete_transient($transient_key);
    delete_transient($lockout_counter_key);
}
add_action('wp_login', 'login_success', 10, 1);

This function removes the transients storing the failed login attempts and lockout counter upon successful authentication.

Step 4: Displaying Time Remaining in Lockout

It’s good practice to provide the user with a human-readable count of how much time is left before trying to log on again.

function time_to_go( $timestamp ) {
    $now = time();
    return human_time_diff( $now, $timestamp );
}

This utility function calculates the time difference between the current time and the lockout expiration, ensuring users know when they can try again.

Step 5: Loading the Child Theme’s Text Domain for Translations

In order to make multilingual-friendly custom login limit messages, you need to load the child theme’s text domain; then you’ll be able to localize the text strings so they can be translated into various languages.

// Load the child theme's text domain
function child_theme_setup() {
    load_child_theme_textdomain( 'simplelimitedlogin', get_stylesheet_directory() . '/languages' );
}
add_action( 'after_setup_theme', 'child_theme_setup' );

For exampe, for Greek translations you’ll need to add a .po file called el.po for Greek. Place it in the /languages folder of your child theme directory.

The contents of the el.po file should include the following translation for the error message:

msgid "<strong>ERROR</strong>: You have reached the authentication limit. Please try again in %s."
msgstr "<strong>ΣΦΑΛΜΑ</strong>: Έχετε φτάσει το όριο προσπαθειών. Δοκιμάστε ξανά σε %s."

This will ensure that Greek-speaking users receive the appropriate message in their language if they exceed the login attempt limit.

Conclusion

What this solution provides is another layer of security to your WordPress site. The custom authentication checks the number of login attempts and makes it robust against brute force attack, while returning proper information about when the user should try logging in after a few failed login attempts.

Best of all, this does not depend on any third-party plugins. All you need to do is add the functions to your theme’s functions.php. Most plugins that can pull in such functionality often need subscriptions for the full range of options. In this custom solution, you enhance the security with no additional cost and dependency. The fewer your dependencies are, the faster and leaner your site will remain.

Additionally, you may consider configuring your site to send email notifications to the administrator for failed login attempts. This extra step can provide valuable oversight and keep you informed about potential security issues.

For more WordPress tips and tech tutorials, stay tuned to our blog!

Subscribe to our newsletter!

Dimitrios S. Sfyris https://aspectsoft.gr/en/

Dimitrios S. Sfyris is a leading expert in systems engineering and web
architectures. With years of experience in both academia and industry, he has published numerous articles and research papers. He is the founder of AspectSoft, a company that developed the innovative e-commerce platform AspectCart, designed to revolutionize the way businesses operate in the e-commerce landscape. He also created the Expo-Host platform for 3D interactive environments.

https://www.linkedin.com/in/dimitrios-s-sfyris/

You May Also Like

More From Author

+ There are no comments

Add yours