Preventing Bot Attacks on WordPress using No Captcha reCaptcha

By  on  

There are many different solutions to prevent bots from submitting web forms; one of the most popular solutions is reCaptcha. reCaptcha actually displays an image with some text in it and user has to enter the text to submit the form successfully. It was difficult for bots to read the text on the image, but as bots algorithms become more advanced, they started breaking this security. It was no more safe. This old method is pretty bad in terms of user friendliness. Then Google created a new reCaptcha called No Captcha reCaptcha.

In this tutorial we will look at what exactly No Captcha reCaptcha is and how to create a plugin which integrates reCaptcha in WordPress Login, Registration and Comment forms to prevent various types of attacks.

Recaptcha Robot

A Look at No Captcha reCaptcha

No Captcha reCaptcha just displays a checkbox asking the user to check it if he/she is not a bot. It might look very hackable but internally Google uses advanced algorithms and methods to find if the user is a bot or not. This new model is more user friendly and secure than the old one.

How Does it Work?

It may seem like a simple checkbox but it's not a checkbox at all. Its a graphics that behaves like a checkbox. Most bots don't run JavaScript so they cannot emulate it. But for the bots which can emulate, this is tracked down by mouse movement and Google's Adsense fraud click detection algorithms.

Registerating a No Captcha reCaptcha App

Users who install this plugin need to register their website to retrieve a site key and secret key.

You need to create a settings page for the plugin which allows the WordPress administrator to install the site key and secret key they retrieved from reCaptcha admin panel.

function 
no_captcha_recaptcha_menu()
{
    add_menu_page( "reCapatcha Options", "reCaptcha Options", "manage_options", "recaptcha-options", "recaptcha_options_page", "", 100 );
}

function recaptcha_options_page()
{
    ?>
        <div class="wrap">
        <h1>reCaptcha Options</h1>
        <form method="post" action="options.php">
            <?php
                settings_fields( "header_section" );
                do_settings_sections( "recaptcha-options" );
                submit_button();                     
            ?>          
        </form>
    </div>
    <?php
}

add_action( "admin_menu", "no_captcha_recaptcha_menu" );

function display_recaptcha_options()
{
    add_settings_section( "header_section", "Keys", "display_recaptcha_content", "recaptcha-options" );

    add_settings_field( "captcha_site_key", __("Site Key"), "display_captcha_site_key_element", "recaptcha-options", "header_section" );
    add_settings_field( "captcha_secret_key", __("Secret Key"), "display_captcha_secret_key_element", "recaptcha-options", "header_section" );

    register_setting( "header_section", "captcha_site_key" );
    register_setting( "header_section", "captcha_secret_key" );
}

function display_recaptcha_content()
{
    echo __( '<p>You need to <a href="https://www.google.com/recaptcha/admin" rel="external">register you domain</a> and get keys to make this plugin work.</p>' );
    echo __( "Enter the key details below" );
}
function display_captcha_site_key_element()
{
    ?>
        <input type="text" name="captcha_site_key" id="captcha_site_key" value="<?php echo get_option('captcha_site_key'); ?>" />
    <?php
}
function display_captcha_secret_key_element()
{
    ?>
        <input type="text" name="captcha_secret_key" id="captcha_secret_key" value="<?php echo get_option('captcha_secret_key'); ?>" />
    <?php
}

add_action( "admin_init", "display_recaptcha_options" );

Let's see how the above code works:

  • We created a settings page on the WordPress admin dashboard.
  • This settings page displays two input text fields for site key and secret key.
  • These keys are stored as WordPress options. We name the options as site_key and secret_key

Preventing Comment Spams

You need to integrate reCaptcha in front end comments forms to prevent bots from putting spam comments.

Create a style.css file in your plugin directory and place this code

#submit { display: none; }

The above code hides the submit button in the WordPress comment form so that we can place the reCaptcha box above the submit button by inserting both submit button and reCaptcha box manually.

Here is the code to integrate reCaptcha on comment forms

add_action( "wp_enqueue_scripts", "frontend_recaptcha_script" );

function frontend_recaptcha_script()
{
    if( get_option( 'captcha_site_key' ) && get_option( 'captcha_secret_key' ) )
    {
        wp_register_script( "recaptcha", "https://www.google.com/recaptcha/api.js" );
        wp_enqueue_script( "recaptcha" );

        $plugin_url = plugin_dir_url( __FILE__ );

        wp_enqueue_style( "no-captcha-recaptcha", $plugin_url . "style.css" );
    }   
}

add_action( "comment_form", "display_comment_recaptcha" );

function display_comment_recaptcha()
{
    if( get_option( 'captcha_site_key' ) && get_option( 'captcha_secret_key' ) )
    {
        ?>
            <div class="g-recaptcha" data-sitekey="<?php echo get_option( 'captcha_site_key' ); ?>"></div>
            <input name="submit" type="submit" value="Submit Comment">
        <?php   
    }

}

add_filter( "preprocess_comment", "verify_comment_captcha" );

function verify_comment_captcha( $commentdata )
{
    if( isset( $_POST['g-recaptcha-response'] ) )
    {
        $recaptcha_secret = get_option( 'captcha_secret_key' );
        $response = file_get_contents( "https://www.google.com/recaptcha/api/siteverify?secret=" . $recaptcha_secret . "&response=" .$_POST['g-recaptcha-response'] );
        $response = json_decode( $response, true );
        if( true == $response["success"] )
        {
            return $commentdata;
        }
        else
        {
            echo __( "Bots are not allowed to submit comments." );
            return null;
        }
    }
    else
    {
        if( get_option( 'captcha_site_key' ) && get_option( 'captcha_secret_key' ) )
        {
            echo __( "Bots are not allowed to submit comments. If you are not a bot then please enable JavaScript in browser." );
            return null;    
        }   
        else
        {
            return $commentdata;
        }
    }
}

Let's see how the above code works:

  • We en-queued Google's reCaptcha JavaScript file to WordPress frontend by using wp_enqueue_scripts action.
  • We also en-queued the style.css file using wp_enqueue_style
  • Inside the comment form we display the checkbox using comment_form action.
  • When the comment is submitted and before inserting it to the database, WordPress calls the preprocess_comment filter. Inside the filter we check if the user is human or bot. If human then we return the comment to be inserted otherwise we return null to prevent the comment from being added to database.

Preventing Brute Force Login Attacks

We need to integrate reCaptcha in the admin login form to prevent bots from running a brute force attack to crack passwords. Here is the code to integrate it on admin login form

add_action( "login_enqueue_scripts", "login_recaptcha_script" );

function login_recaptcha_script()
{
    if( get_option( 'captcha_site_key' ) && get_option( 'captcha_secret_key' ) )
    {
        wp_register_script( "recaptcha_login", "https://www.google.com/recaptcha/api.js" );
        wp_enqueue_script( "recaptcha_login" );
    }
}

add_action( "login_form", "display_login_captcha" );

function display_login_captcha()
{
    if( get_option( 'captcha_site_key' ) && get_option( 'captcha_secret_key' ) )
    {
        ?>
            <div class="g-recaptcha" data-sitekey="<?php echo get_option('captcha_site_key' ); ?>"></div>
        <?php
    }   
}

add_filter( "wp_authenticate_user", "verify_login_captcha", 10, 2 );

function verify_login_captcha( $user, $password )
{
    if( isset( $_POST['g-recaptcha-response'] ) )
    {
        $recaptcha_secret = get_option( 'captcha_secret_key' );
        $response = file_get_contents( "https://www.google.com/recaptcha/api/siteverify?secret=" . $recaptcha_secret . "&response=" . $_POST['g-recaptcha-response'] );
        $response = json_decode( $response, true );
        if( true == $response["success"] )
        {
            return $user;
        }
        else
        {
            return new WP_Error( "Captcha Invalid", __( "<strong>ERROR</strong>: You are a bot" ) );
        } 
    }
    else
    {
        if( get_option( 'captcha_site_key' ) && get_option( 'captcha_secret_key' ) )
        {
            return new WP_Error( "Captcha Invalid", __( "<strong>ERROR</strong>: You are a bot. If not then enable JavaScript" ) );
        }
        else
        {
            return $user;
        }
    }   
}

Let's see how the above code works:

  • We en-queued Google's reCaptcha JavaScript file to WordPress admin login, registration and lost password pages by using the login_enqueue_scripts action.
  • We displayed the checkbox using the login_form action.
  • Before producing the final authentication result, WordPress runs the wp_authenticate_user filter to let us add a extra validation step. We check if the user is bot or human inside this filter. If its human we return the user object else we return and WordPress error object.

Preventing Creation of Fake Accounts

We need to integrate reCaptcha in the admin registration form to prevent bots from creating fake accounts. Here is the code to integrate it on admin registration form

add_action( "register_form", "display_register_captcha" );

function display_register_captcha()
{
    if( get_option( 'captcha_site_key' ) && get_option( 'captcha_secret_key' ) )
    {
        ?>
            <div class="g-recaptcha" data-sitekey="<?php echo get_option( 'captcha_site_key' ); ?>"></div>
        <?php   
    }       
}

add_filter( "registration_errors", "verify_registration_captcha", 10, 3 );

function verify_registration_captcha( $errors, $sanitized_user_login, $user_email )
{
    if( isset( $_POST['g-recaptcha-response'] ) )
    {
        $recaptcha_secret = get_option( 'captcha_secret_key' );
        $response = file_get_contents( "https://www.google.com/recaptcha/api/siteverify?secret=" . $recaptcha_secret . "&response=" . $_POST['g-recaptcha-response'] );
        $response = json_decode( $response, true );
        if( true == $response["success"] )
        {
            return $errors;
        }
        else
        {
            $errors->add( "Captcha Invalid", __( "<strong>ERROR</strong>: You are a bot" ) );
        }
    }
    else
    {   
        if( get_option( 'captcha_site_key' ) && get_option( 'captcha_secret_key' ) )
        {
            $errors->add( "Captcha Invalid", __( "<strong>ERROR</strong>: You are a bot. If not then enable JavaScript" ) );
        }
        else
        {
            return $errors;
        }

    }   

    return $errors;
}

Let's see how the above code works:

  • We displayed the checkbox using register_form action.
  • Before producing the final authentication result WordPress runs the registration_errors filter to let us add a extra validation step. We check if the user is bot or human inside this filter. If its human we return empty error object else we add a add to the error object and return it.

Preventing Bots Submitting Lost Password Form

We need to integrate reCaptcha in the admin lost password form to prevent bots from submitting this form. Here is the code to integrate it on admin lost password form

add_action( "lostpassword_form", "display_login_captcha" );
add_action( "lostpassword_post", "verify_lostpassword_captcha" );

function verify_lostpassword_captcha()
{
    if( isset( $_POST['g-recaptcha-response'] ) )
    {
        $recaptcha_secret = get_option( 'captcha_secret_key' );
        $response = file_get_contents( "https://www.google.com/recaptcha/api/siteverify?secret=" . $recaptcha_secret . "&response=" . $_POST['g-recaptcha-response'] );
        $response = json_decode( $response, true );
        if( true == $response["success"] )
        {
            return;
        }
        else
        {
            wp_die( __( "<strong>ERROR</strong>: You are a bot" ) );
        }
    }
    else
    {
        if( get_option( 'captcha_site_key' ) && get_option( 'captcha_secret_key' ) )
        {
            wp_die( __( "<strong>ERROR</strong>: You are a bot. If not then enable JavaScript" ) ); 
        }
        else
        {
            return;
        }

    }   

    return $errors; 
}

Let's see how the above code works:

  • We displayed the checkbox using lostpassword_form action.
  • Before producing the final password reset link WordPress runs the lostpassword_post action to let us add a extra validation step. We check if the user is bot or human inside this filter. If its human we return nothing else we kill the script with an error message.

Final Thoughts

Its a new way to protect your website forms from bots and increase user friendliness. You can also learn how Google detects bot or human internally using this new type of captcha. Once you have integrated this plugin in your WordPress site write your experiences below.

Narayan Prusty

About Narayan Prusty

Narayan is a web astronaut. He is the Founder of QScutter. He loves to share ideas. When not coding he enjoys playing football. You will often find him at QNimate, his personal blog.

Recent Features

  • By
    Write Better JavaScript with Promises

    You've probably heard the talk around the water cooler about how promises are the future. All of the cool kids are using them, but you don't see what makes them so special. Can't you just use a callback? What's the big deal? In this article, we'll...

  • By
    Create Namespaced Classes with MooTools

    MooTools has always gotten a bit of grief for not inherently using and standardizing namespaced-based JavaScript classes like the Dojo Toolkit does.  Many developers create their classes as globals which is generally frowned up.  I mostly disagree with that stance, but each to their own.  In any event...

Incredible Demos

Discussion

  1. Thanks for explanation. I will use this for my site.

  2. Chris

    Great article.
    Is there any way to make this captcha responsive?

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!