How to avoid junk contact messages without the use of a captcha field

stop spam

Throughout this article we'll be covering a technique that allows our messaging system to be able to find out fake feedback messages. That's right! Every time a SPAM machine attempts to send us a junk contact message, it'll be automatically ignored. In order to achieve our goal, we're not going to use any captcha field, but a technique based on how much time it takes to fill out the contact form.

If you want to see right now the final result of this article, please click on see a demo.

contact form

To do that, we're going to create two different pieces of code. The first piece of code is for validating all the fields of our contact form (by using JavaScript). The second one is for finding out whether the message was submitted by a SPAM machine (by using PHP). If everything is OK, the message will be sent. Otherwise an error message will be displayed.

Validating the contact form

When submitting a feedback message, it's of primary importance that all the fields are appropriately filled out. This means that the fields for the name, email, title and message must be checked to see the following:

  1. The fields cannot be blank.
  2. The information given by the user must be correct. Except for the email field, we're going to assume that the contact details given by the user are true. If you want to get a reply, it makes no sense to give false information.
  3. The format for the email address must be checked for validation. This prevents the user from writing an email address with an invalid format.

If this process is successfully completed, then all the information is given to a PHP script. This script is responsible for finding out whether the message was submitted by a SPAM machine.

In order to get the whole source code, please go to my page on Github.

/**
 * Checks to see if a field was left blank.
 * @param field
 *   The field to be evaluated.
 * @return
 *   Returns TRUE if the field was left blank.
 */
function isItBlank(field) {

  if (field == null || field == '') {

    return true;

  }

}

/**
 * Checks to see if the format for the email field is valid.
 * @param email
 *   The email address to be evaluated.
 * @return
 *   Returns TRUE if the format for the email address is correct.
 */
function checkEmail(email) {

  filter = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,4})+$/;

  if (filter.test(email.value)) {

    return true;

  } else {

    return false;
	
  }

}

/**
 * Checks to see if all the fields have been filled out properly:
 *   - There is no field in blank.
 *   - The format for the email field is valid.
 * @return
 *   Returns FALSE if there was an error.
 */
function validateForm() {

  var my_form = document.forms['contact_form'];

  var required_fields = new Array('name', 'email', 'subject', 'message');

  var email = document.forms['contact_form'][1];

  var x;

  /**
   * This piece of code goes through the contact form to check if there is no
   * field in blank. If the visitor left any field in blank, then an alert message
   * will be displayed and FALSE returned.
   */

  for (x in required_fields) {

    if (isItBlank(my_form[required_fields[x]].value)) {

      my_form[required_fields[x]].focus();
			
      alert('The ' + required_fields[x] + ' field has been left blank.');

      return false;

    }

  }

  /**
   * Checks to see if the format for the email address is valid. If it's
   * not, then, an alert message will be displayed and FALSE returned.
   */
  if (!checkEmail(email)) {

    email.focus();
			
    alert('The email field is not valid.');

    return false;
 
  }

}

Although the previous code is self explanatory, let's look closely at how the email address is validated:

/**
 * Checks to see if the format for the email field is valid.
 * @param email
 *   The email address to be evaluated.
 * @return
 *   Returns TRUE if the format for the email address is correct.
 */
function checkEmail(email) {

  filter = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,4})+$/;

  if (filter.test(email.value)) {

    return true;

  } else {

    return false;
	
  }

}

As you can see, there is a variable named filter that stores a very weird string value. This is what we call Regular Expression Pattern. Let's explain the meaning of each symbol or set of symbols:

  • / .. /, all regular expressions start and end with forward slashes.
  • ^, the circumflex accent matches the beginning of the string or line.
  • \w+, matches one or more word characters. It also includes the underscore. It's equivalent to [a-zA-Z0-9_].
  • [\.-], square brackets are for grouping a set of characters. The backward slash indicates that the next character i special and must not be interpreted literally. This set of symbols matches either the dot or the hyphen-minus character.
  • ?, the previous character is able to turn up either once or zero times. In this case, the previous character is [\.-].
  • *, matches the previous character zero or more times. Here the previous character is ([\.-]?\w+).
  • @, matches only the commercial at character.
  • \.\w{2,4}, matches a dot followed by two or three or four characters. E.g., .uk, .com, .info.
  • +, the plus sign matches zero or more occurrences of the previous character is (\.\w{2.4}).
  • $, the dollar sign matches the end of the string or line.

Now let's look closely at how an email address is made. An email address is a subset of ASCII characters separated into two parts: the username and domain. The username part can be up to 64 characters long and contains the following characters:

  • Uppercase (A-Z) and lowercase (a-z) letters.
  • Digits (0-9)
  • The character . (dot) if it isn't the first or last character and it won't come one after another.
  • The characters (! # $ % & ' * + - / = ? ^ _ ` { | } ~). These characters, except for the underscore, have been filtered out by the above regular expression due to the fact that they hardly ever are used by users.

The domain name part can be up to 253 characters long and contains letters, digits, hyphens and dots.

Was the contact message submitted by a true user?

Now we're going to go ahead and create the PHP script for finding out fake messages. This is how it works:

First, a hidden form field stores when the contact form was displayed to the user for the first time. In addition a variable named time1 stores its value. This hidden field looks like this:

<input type="hidden" id ="time1" value="<?php echo time(); ?>" />

Second, a variable named time2 stores when the contact form was submitted by the user.

The time() function returns the current Unix timestamp, that is, the number of seconds since the Unix Epoch (January 1 1970 00:00:00 GMT).

Finally, the value stored by time1 is deducted from the value stored by time2. If the final result is less than a predefined value (for example, 5 seconds), then the script will flag the message as a fake message. This is because no visitor would be able to fill out the contact form in less than five seconds.

<?php
// Check for a form submission.
if ($_SERVER['REQUEST_METHOD'] == 'POST') {

  // Threshold
  $th = 5;

 // Stores when the contact form was displayed to the user for the first time.
  $time1 = $_POST['time1'];

 // Stores when the contact form was submitted by the user.
  $time2 = time();

  // Receiver or receivers of the email.
  $to = 'user@example.com';

  // Subject of the email to be sent.
  $subject = '[Site Feedback] ' . $_POST['subject'];

  // Message to be sent.
  $message = wordwrap($_POST['message'], 70, "\r\n");

  // Headers
  $headers = 'From: ' . $_POST['name'] . ' <' . $_POST['email'] . '>';

  if ($time2 - $time1 >= $th) {

    $delivery = mail($to, $subject, $message, $headers);

    if ($delivery) {

      $msg = '<div class="alert alert-success">The message was successfully accepted for delivery.</div>';

    } else {

      $msg = '<div class="alert alert-warning">The message was not successfully accepted for delivery. Please try again.</div>';

    }

  } else {

    $msg = '<div class="alert alert-danger">There was an error!</div>';

  }

}
?>

For obvious reasons, the sending of emails is not available on the demonstration page.