Improve WordPress Security: Limit Login Attempts Without a Plugin

Estimated read time 5 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?

Limiting login attempts is a proven strategy for reducing the risk of brute force attacks. By restricting the number of times a user can attempt to log in, you can effectively stop bots and malicious users from guessing passwords and accessing your website.

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

When users attempt to log in, we need to monitor their activity and count their failed attempts.

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);

This function checks the user’s IP address and monitors the number of failed login attempts. If the user exceeds three failed attempts, they are locked out for a specific period, with a warning message explaining how long they must wait before trying again.

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 failed attempt is tracked using WordPress transients, temporary options stored in the database. When a user reaches three failed attempts, they are locked out, and the lockout duration increases after repeated failures.

Step 3: Resetting on Successful Login

Upon a successful login, it’s essential to clear any lockout data so that users aren’t blocked after authenticating correctly.

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

To improve user experience, it’s helpful to provide a human-readable countdown of how much time remains before they can try logging in 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

To make your custom login limit messages multilingual-friendly, it’s essential to load the child theme’s text domain. This allows you to localize the text strings, ensuring they can be translated into different 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' );

or example, if you want to add Greek translations, you need to create a .po file named el.po for Greek. This file should be placed in the /languages folder inside 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

By implementing this solution, you’ve added an extra layer of security to your WordPress site. This custom code limits login attempts and protects against brute force attacks, locking out users after repeated failures while providing them with useful information on when they can attempt to log in again.

Best of all, this solution doesn’t require any external plugins. You simply need to add the functions to your theme’s functions.php file. Many plugins offer similar features but often require a subscription to unlock their full range of options. With this custom solution, you enhance security without additional costs or dependencies. Minimizing dependencies helps keep your site faster and more efficient.

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