The business of collecting user data includes the ubiquitous form. The thing with forms is that they are the first to see abuse on the Internet and if left unprotected your inbox or database will soon be overrun with spam. One of many methods of protecting forms is the CAPTCHA. CAPTCHAs require that a user interpret a randomly-generated image by typing its value into a text field. Since automated programs (bots) can’t read the image they are stopped cold. Humans and their famously short attention spans happen to have enough bandwidth to tolerate a CAPTCHA or two. The Spammer variants, on the other hand, seem to lack the patience needed and will quickly move on to easier pickings of which there are untold thousands. So then, while a CAPTCHA is a minor inconvenience to your end-users it’s presence more than makes up for itself by its ability to completely stave off an avalanche of useless form posts.

Google provides a CAPTCHA service called reCAPTCHA which is the subject of this article.

The workflow I’ll be implementing is the following:

  1. User “submits” form
  2. The form is validated
  3. If form validation succeeds I then make an AJAX call to the reCaptcha service via a proxy to see if the user entered the correct reCaptcha value
  4. If an incorrect value was entered I’ll display an in-line message to the user
  5. If the correct value was entered I will submit the form

You can see this process in action below:

Register for a set of reCAPTCHA keys

You must first sign up to use reCAPTCHA by visiting http://www.google.com/recaptcha. After you register click the “Add Site” link and add your site. After you add a site you will get 2 keys – keep these handy, we’ll need them later.

Download the reCAPTCHA PHP Library

You will also need to download the reCAPTCHA PHP Library (I’m assuming that your site is on a PHP-compatible server).

Setting up a proxy

Since we plan on making AJAX calls to the reCAPTCHA service we know we will need a proxy as we would otherwise run afoul of the browser’s cross-domain policy. The way we will circumvent this is by creating a proxy. This proxy will handle communication between our web page and reCAPTCHA by forwarding our submitted form data to the reCAPTCHA service which in return responds to the proxy and then the proxy hands the response back to our web page.

The reCAPTCHA PHP library contains everything we need to setup our proxy. The library contains 5 files – we will be using 2 of them:

  • recaptchalib.php
  • example-captcha.php

The second file – example-captcha.php – will need to be modified for our purposes. Here is the original file (this is version 1.11) before any changes:

<html>
  <body>
    <form action="" method="post">
<?php

require_once('recaptchalib.php');

// Get a key from https://www.google.com/recaptcha/admin/create
$publickey = "";
$privatekey = "";

# the response from reCAPTCHA
$resp = null;
# the error code from reCAPTCHA, if any
$error = null;

# was there a reCAPTCHA response?
if ($_POST["recaptcha_response_field"]) {
        $resp = recaptcha_check_answer ($privatekey,
           $_SERVER["REMOTE_ADDR"],
           $_POST["recaptcha_challenge_field"],
           $_POST["recaptcha_response_field"]);

        if ($resp->is_valid) {
            echo "You got it!";
        } else {
             # set the error code so that we can display it
             $error = $resp->error;
        }
}
echo recaptcha_get_html($publickey, $error);
?>
    <br/>
    <input type="submit" value="submit" />
    </form>
  </body>
</html>

To get to where we need to be we need to remove all html and make a couple of tiny changes to the PHP. I won’t walk through them in detail opting instead to just show you the finished edited file (the savvy among you will no doubt notice the differences) and to mention that the public and private reCAPTCHA keys are in use here:

<?php

require_once('recaptchalib.php');

// Get keys from https://www.google.com/recaptcha/admin/create
$publickey = "YOUR_PUBLIC_KEY_HERE";
$privatekey = "YOUR_PRIVATE_KEY_HERE";

# the response from reCAPTCHA
$resp = null;

# was there a reCAPTCHA response?
if ($_POST["recaptcha_response_field"]) {
    $resp = recaptcha_check_answer ($privatekey,
        $_SERVER["REMOTE_ADDR"],
        $_POST["recaptcha_challenge_field"],
        $_POST["recaptcha_response_field"]);

    if ($resp->is_valid) {
        echo "success";
    } else {
        echo "failure";
    }
}
?>

The line that echoes “success” is what we will be listening for in our AJAX call to determine if the reCAPTCHA verification was successful.

At this point your proxy is done. Go ahead and place your modified example-captcha.php and the unchanged recaptchalib.php on your server in the same folder as your form.

Add the jQuery and reCAPTCHA JS Libraries

You need to add these two lines within the head of your document (use whatever version of jQuery your project requires):

...
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js">
<script type="text/javascript" src="http://www.google.com/recaptcha/api/js/recaptcha_ajax.js">
...

Setting up your form

The form code below is pretty basic – things to pay attention to:

  • This is a template for the reCAPTCHA functionality only – you still need to configure your form to work with your form remailer (i.e., gdform.php on GoDaddy, formmail.php, cgiemail or whatever the case might be)
  • Note the onsubmit=”return doForm()” in the FORM tag – this is the hook to our JavaScript that communicates to the reCAPTCHA server
  • There are 2 divs present:

    • <div id=”recap”></div> this is the wrapper for the reCaptcha itself
    • <div id=”err”>…</div> This is the in-line error message that we will display if the reCAPTCHA verification fails.
       
  • Note the style tag – we have a css class called “hidden” that is assigned to the reCAPTCHA error div. If there is an error we use jQuery to remove the class and show the error message to the user
     
...
<style type="text/css">
    /* place the style in your stylesheet or move this to the head of your document */
    .hidden{
        display:none;
    }
</style>

<!-- note the onsubmit="return doForm()" - your FORM tag must contain this -->
<form action="test.php" method="post" name="form1" id="form1" onsubmit="return doForm()">

    <!-- your form fields would go in here -->
    Name: <input class="textInput" name="Name" type="text" id="name" size="55" maxlength="55" />

    <!-- The reCAPTCHA wrapper is here - this is required!! -->
    <div id="recap"></div>

    <!-- the reCAPTCHA error div is next, edit/style as you see fit -->
    <div id="err" class="hidden" style="background-color:#FFFF00;color:#FF0000;margin:12px 0px 12px 0px;">The Captcha was not entered correctly. Please try again.</div>

    <!-- the submit button -->
    <input type="submit" name="button" id="button" value="Submit" />
</form>
...

Inserting the reCAPTCHA, validating Form and reCAPTCHA input

Next we need to insert the reCAPTCHA elements into the document. In the code that follows you can see that I’m inserting the reCAPTCHA on jQuery’s document.ready event.

When the form is submitted I first validate the form input which you can see in the doForm() function. If validation passes I then attempt to validate the reCAPTCHA via the recapVerify function.

Its all put together in the code snippet below (** add your public key where indicated in line 41 **):

...
<script type="text/javascript">

    // VALIDATE THE FORM
    function doForm(){
	// your form validation would go here, for example:
	var isValid = $('#name').val().length != 0 ? true : false;

	if (!isValid){ // FORM VALIDATION FAILED
	    alert('Name is required');

	} else { //FORM VALIDATION SUCCEEDED, validate the reCAPTCHA
	    recapVerify(); // verify reCAPTCHA
	}
	return false;
    }

    // VALIDATE THE reCAPTCHA
    function recapVerify(){
        $.ajax({
	    type:'post',
	    url: 'captcha_proxy.php',
	    data: {
	        recaptcha_challenge_field:$('#recaptcha_challenge_field').val(),
	        recaptcha_response_field:$('#recaptcha_response_field').val()
	    }
	}).done(function(data, textStatus, jqXHR){
	    if (data == 'success'){
	        $('#err').addClass('hidden');
	        document.forms[0].submit();
	    } else {
	        $('#err').removeClass('hidden');
	    }
	}).fail(function(jqXHR,textStatus,errorThrown){
            console.log('proxy or service failure');
	});
    }

    // WHEN CALLED THIS INSETS THE reCAPTCHA INTO THE PAGE
    function reCapInsert(){
        Recaptcha.create('YOUR_PUBLIC_KEY_HERE',  // public key
        'recap',
            {
                theme: 'clean',
                callback: Recaptcha.focus_response_field
            }
	);
    }

    // WHEN THE DOM HAS LOADED FIRE THE reCapInsert FUNCTION TO INSERT THE reCAPTCHA
    $( document ).ready(function(){
        reCapInsert();
    });
	
</script>
...

And that should do it. Download all files here.