Captcha (keeping those spammers out of your “business”) Part 03: Adding a user interface to the captcha

In Part 03 of this series of posts we are going to build a simple input form to hold the captcha image.

First, I will write out some HTML and in the <body> I will include a simple form.

<form method="post" action="" id="comment_form">
  <ul>
    <li>
      <label for="form_name">Name</label>
      <input type="text" name="form_name" id="form_name" />
    </li>
    <li>
      <label for="form_email">Email</label>
      <input type="text" name="form_email" id="form_email" />
    </li>
    <li>
      <label for="form_captcha"> </label>
      <img id="form_captcha" />
    </li>
    <li>
      <label for="form_captcha_instructions"> </label>
      <span id="form_captcha_instructions">enter the code exactly as shown above</span>
    </li>
    <li>
      <label for="form_captcha_value">Code<em>*</em></label>
      <input type="text" name="form_captcha_value" id="form_captcha_value" />
    </li>
    <li>
      <label for="form_comment">Comment<em>*</em></label>
      <textarea id="form_comment" name="form_comment" rows="10"></textarea>
    </li>
    <li>
      <label for="submit"> </label>
      <input type="button" name="submit" value="Add Comment" id="submit"/>
    </li>
  </ul>
</form>

Curious to see how horrible this looks? Below is a snapshot:

Captcha Form, No Style — Literally

Before we continue with the Javascript, I will add some CSS to cleanup the layout:

      *
      {
        font: 10pt Arial;
        margin: 0;
        padding: 0;
      }
      #comment_form
      {
        margin: 5px;
        padding: 5px;
        border: 1px Solid #e5e5e5;
        width: 400px;
        background: #f9f9f9;
      }
        #comment_form ul
        {
          list-style: none;
        }
        #comment_form ul li
        {
          margin-bottom: 5px;
        }
            #comment_form ul li label
            {
              width: 75px;
              float: left;
            }
            #comment_form ul li textarea
            {
              border: 1px Solid #e5e5e5;
              width: 313px;
            }
            #comment_form ul li input
            {
              border: 1px Solid #e5e5e5;
              width: 313px;
            }
              #comment_form ul li label em
              {
                color: red;
              }

There, some simple CSS and things are starting to look a lot better.

Captcha Form, with some style

To include the captcha image within the form I will use some javascript to call the captcha.php. This is easily accomplished by using some very simple Javascript to modify the DOM.

index.php cont…

      function getCaptchaImage(session_id)
      {
        document.getElementById("form_captcha").src="captcha.php?action=challenge&session_id=" + session_id;
      }

The getCaptchaImage() function adds the ’src’ attribute to the <img> in our HTML. We can call this function in the <body> by using <body onLoad=”getCaptchaImage(session_id);”>. But the session_id value will need to be determined by some PHP.

Up to this point, here is the index.php:

index.php

<?php
session_start();
?>
<html>
  <head>
    <style type="text/css">
      *
      {
        font: 10pt Arial;
        margin: 0;
        padding: 0;
      }
      #comment_form
      {
        margin: 5px;
        padding: 5px;
        border: 1px Solid #e5e5e5;
        width: 400px;
        background: #f9f9f9;
      }
        #comment_form ul
        {
          list-style: none;
        }
        #comment_form ul li
        {
          margin-bottom: 5px;
        }
            #comment_form ul li label
            {
              width: 75px;
              float: left;
            }
            #comment_form ul li textarea
            {
              border: 1px Solid #e5e5e5;
              width: 313px;
            }
            #comment_form ul li input
            {
              border: 1px Solid #e5e5e5;
              width: 313px;
            }
              #comment_form ul li label em
              {
                color: red;
              }
    </style>
    <script language="Javascript">
      function getCaptchaImage(session_id)
      {
        document.getElementById("form_captcha").src="captcha.php?action=challenge&session_id=" + session_id;
      }
    </script>
  </head>
<body onLoad="getCaptchaImage('<?php echo session_id();?>');">
<form method="post" action="" id="comment_form">
  <ul>
    <li>
      <label for="form_name">Name</label>
      <input type="text" name="form_name" id="form_name" />
    </li>
    <li>
      <label for="form_email">Email</label>
      <input type="text" name="form_email" id="form_email" />
    </li>
    <li>
      <label for="form_captcha"> </label>
      <img id="form_captcha" />
    </li>
    <li>
      <label for="form_captcha_instructions"> </label>
      <span id="form_captcha_instructions">enter the code exactly as shown above</span>
    </li>
    <li>
      <label for="form_captcha_value">Code<em>*</em></label>
      <input type="text" name="form_captcha_value" id="form_captcha_value" />
    </li>
    <li>
      <label for="form_comment">Comment<em>*</em></label>
      <textarea id="form_comment" name="form_comment" rows="10"></textarea>
    </li>
    <li>
      <label for="submit"> </label>
      <input type="button" name="submit" value="Add Comment" id="submit" />
    </li>
  </ul>
</form>
</body>
</html>

And our result:

Captcha Form, dynamically loading the captcha

A quick check of our database verifies that the correct captcha values have been saved to the database (our PHP is working!) :

mysql> select * from captcha;
+----------------------------+-----------+
| sid                        | challenge |
+----------------------------+-----------+
| upgf36fgm8o9b1a381fmk90gf4 | 406fb     |
+----------------------------+-----------+
1 row in set (0.00 sec)

Now we need a way to determine if the user has entered the correct captcha code to the input box. The check will be completed when the user clicks the “Add Comment” button. By using some Ajax we can easily determine if the code is correct.

index.php cont…

      function createRequestObject()
      {
        var reqObj = null;
        //Attempt to create native XMLHttpRequest Object
        if(window.XMLHttpRequest)
        {
          try
          {
            reqObj = new XMLHttpRequest();
          }
          catch(error)
          {
            reqObj = null;
          }
        }
        //Attempt to create ActiveX version
        else if(window.ActiveXObject)
        {
          try
          {
            reqObj = new ActiveXObject("Msxml2.XMLHTTP");
          }
          catch(error)
          {
            try
            {
              reqObj = new ActiveXObject("Microsoft.XMLHTTP");
            }
            catch(error)
            {
              reqObj = null;
            }
          }
        }
        return reqObj;
      }

      var ro = createRequestObject();

      function testChallenge(sid)
      {
        var challenge = document.getElementById('form_captcha_value').value;
        ro.open('get', 'captcha.php?session_id=' + sid + '&action=verify&challenge=' + challenge, true);
        ro.onreadystatechange = getChallengeResponse;
        ro.send(null);
      }

      function getChallengeResponse()
      {
        if(ro.readyState == 4)
        {
          alert(ro.responseText);
        }
      }

In the <script> I have added 3 new functions and 1 variable. The createRequestObject() function is responsible for creating our repsonse object based on the user’s browser type (IE or a browser that natively support XMLHttpRequest). The testChallenge() function sends the request to our captcha.php and waits for a response. The getChallengeResponse() will display an alert with the response from our captcha.php when it has completed processing on the server.

Now to trigger the testChallenge() function we will modify the “Add Comment” button.

index.php complete

<?php
session_start();
?>
<html>
  <head>
    <style type="text/css">
      *
      {
        font: 10pt Arial;
        margin: 0;
        padding: 0;
      }
      #comment_form
      {
        margin: 5px;
        padding: 5px;
        border: 1px Solid #e5e5e5;
        width: 400px;
        background: #f9f9f9;
      }
        #comment_form ul
        {
          list-style: none;
        }
        #comment_form ul li
        {
          margin-bottom: 5px;
        }
            #comment_form ul li label
            {
              width: 75px;
              float: left;
            }
            #comment_form ul li textarea
            {
              border: 1px Solid #e5e5e5;
              width: 313px;
            }
            #comment_form ul li input
            {
              border: 1px Solid #e5e5e5;
              width: 313px;
            }
              #comment_form ul li label em
              {
                color: red;
              }
    </style>
    <script language="Javascript">
      function createRequestObject()
      {
        var reqObj = null;
        //Attempt to create native XMLHttpRequest Object
        if(window.XMLHttpRequest)
        {
          try
          {
            reqObj = new XMLHttpRequest();
          }
          catch(error)
          {
            reqObj = null;
          }
        }
        //Attempt to create ActiveX version
        else if(window.ActiveXObject)
        {
          try
          {
            reqObj = new ActiveXObject("Msxml2.XMLHTTP");
          }
          catch(error)
          {
            try
            {
              reqObj = new ActiveXObject("Microsoft.XMLHTTP");
            }
            catch(error)
            {
              reqObj = null;
            }
          }
        }
        return reqObj;
      }

      var ro = createRequestObject();

      function testChallenge(sid)
      {
        var challenge = document.getElementById('form_captcha_value').value;
        ro.open('get', 'captcha.php?session_id=' + sid + '&action=verify&challenge=' + challenge, true);
        ro.onreadystatechange = getChallengeResponse;
        ro.send(null);
      }

      function getChallengeResponse()
      {
        if(ro.readyState == 4)
        {
          alert(ro.responseText);
        }
      }  

      function getCaptchaImage(session_id)
      {
        document.getElementById("form_captcha").src="captcha.php?action=challenge&session_id=" + session_id;
      }
    </script>
  </head>
  <body onLoad="getCaptchaImage('<?php echo session_id();?>');">
  <form method="post" action="" id="comment_form">
    <ul>
      <li>
        <label for="form_name">Name</label>
        <input type="text" name="form_name" id="form_name" />
      </li>
      <li>
        <label for="form_email">Email</label>
        <input type="text" name="form_email" id="form_email" />
      </li>
      <li>
        <label for="form_captcha"> </label>
        <img id="form_captcha" />
      </li>
      <li>
        <label for="form_captcha_instructions"> </label>
        <span id="form_captcha_instructions">enter the code exactly as shown above</span>
      </li>
      <li>
        <label for="form_captcha_value">Code<em>*</em></label>
        <input type="text" name="form_captcha_value" id="form_captcha_value" />
      </li>
      <li>
        <label for="form_comment">Comment<em>*</em></label>
        <textarea id="form_comment" name="form_comment" rows="10"></textarea>
      </li>
      <li>
        <label for="submit"> </label>
        <input type="button" name="submit" value="Add Comment" id="submit" onClick="testChallenge('<?php echo session_id(); ?>');"/>
      </li>
    </ul>
  </form>
  </body>
</html>

Now lets refresh our index.php and run it from the top.

Step 1: Viewing the form

Captcha Form, Running the Scripts from the top

Step 2: Verify the database has the correct challenge code

mysql> select * from captcha;
+----------------------------+-----------+
| sid                        | challenge |
+----------------------------+-----------+
| upgf36fgm8o9b1a381fmk90gf4 | 8d2ac     |
+----------------------------+-----------+
1 row in set (0.00 sec)

Step 3: Adding the correct captcha challenge code into the input box and the result after clicking “Add Comment”

Captcha Form, Running the Scripts from the top 2

Step 4: Adding the incorrect captcha challenge code into the input box and the result after clicking “Add Comment”

Captcha Form, Running the Scripts from the top 3

Everything is working just the way it was expected too. Now that we have the fundamentals of building a simple captcha method there are some additional issues that require future planning:

  1. Accessibility is a large concern with this captcha as it currently sits. Users with visual impairments may not be able to use this method to access your site. How can we add to our script to ensure all potential users are able to utilize our sites?
  2. I would not consider this captcha to be all that strong. More advanced captcha methods would utilize more sophisticated methods to prevent OCR bots from breaking our spam prevention.

This series of posts was not intended to be an absolute guide to developing captcha. The purpose of this post was to demonstrate one possible method to build a captcha from the ground up in 1 hour or less.

Please feel free to provide feedback or request clarification, but remember “I am NOT a web designer!”

2 Responses to “Captcha (keeping those spammers out of your “business”) Part 03: Adding a user interface to the captcha”

  1. ridhocyber Says:

    where the databse structure, and database query?
    i can’t find it!!!

  2. acsummer Says:

    This is the third and final post for the captcha. The second post covers the server side script in PHP which explains the database structure and the query.

    You can find the second post here: http://acsummer.wordpress.com/2007/12/21/captcha-keeping-those-spammers-out-of-your-%e2%80%9cbusiness%e2%80%9d-part-02-extending-the-php-the-add-the-challenge-code-to-a-database/

    If you have any further questions, please do not hesitate to contact me.

Leave a Reply