Toy robots behind the window

Stop Form Spam Robots Using HoneyPot and Time Measuring

Let’s stop the robots, stop form spam!

One of the most common problems when having any kind of forms on a site or blog is receiving spam. The more popular the site is, the more likely it will become a target for spamming entities and more difficult is to stop form spam. That's why it's important to protect it from the very beginning.

Most of the spamming entities are spam robots. It's rare when a human sits there and fill forms manually. Taking that as an assumption, it's relatively easy to trick those robots and make them fail their goal.

The use of Captcha is an easy and effective way of fighting spam but it comes with a cost which our users have to pay, and this is fulfilling one more step in the process.

[tweet_box design="default" float="none"]HoneyPot and Time Measuring are a great alternative to Captcha in order to stop spam robots hitting our forms.[/tweet_box]

One method that has worked for me to stop form spam is the use of HoneyPot and Time Measuring. These two techniques are powerful when used together.

So let's start with a sample form which has 3 fields: Name, Email address and Message:

<form action="backend.php" method="post">
    <label>Full name:</label>
    <input type="text" name="fullName" />
    <label>Email address:</label>
    <input type="email" name="emailAddress" />
    <label>Your message</label>
    <textarea name="message" />
</form>

A spam robot will attempt to fill all the fields with data in order to send the form and this is what I'm going to use to our advantage.

First, let’s implement the HoneyPot. Stop form spam with sweetness!

use HoneyPot to stop form spam robots

Picture: bossfight.co

The goal here is to have a dummy field hidden by CSS and trick the spam robot into fulfilling it. Then I check in my backend validation whether the field has value, if so, then I assume that the request is spam.

Firstly, I add a field to the form and this will be my HoneyPot field. The important thing here is to make our new field to blend in. For our example, I'm going to add the "Phone number" and the label text should be "Leave this field empty". The specific label text is for the users in browsers with the CSS disabled, so they know they have to leave it empty.

<form action="backend.php" method="post">
    <label>Full name:</label>
    <input type="text" name="fullName" />
    <label>Email address:</label>
    <input type="email" name="emailAddress" />
    <label>Leave this field empty:</label>
    <input type="text" name="phoneNumber6tY4bPYk" autocomplete="off" />
    <label>Your message</label>
    <textarea name="message" />
</form>

Two important things to notice.
The input field has  autocomplete="off". The name attribute is  "phoneNumber6tY4bPYk", it contains a small hash after the "phoneNumber" part. This is to keep the browsers from inserting data by default or autocompletion.

In addition, I make then new field invisible by CSS:

<form action="backend.php" method="post">
    <label>Full name:</label>
    <input type="text" name="fullName" />
    <label>Email address:</label>
    <input type="email" name="emailAddress" />
    <label class="hide">Leave this field empty:</label>
    <input class="hide" type="text" name="phoneNumber6tY4bPYk" autocomplete="off" />
    <label>Your message</label>
    <textarea name="message" />
</form>
.hide {
    display: none;
}

Now, let's take a look to the backend validation. I check the HoneyPot field value ($honeyPot). If it's not empty, the most probable is that the form was sent by a spam robot:

...

$honeyPot = $_POST['phoneNumber6tY4bPYk'];

if (trim($honeyPot) != '') {
    // This is a spam robot. Take action!
}

// It's a human. Continue with the rest of the validation.
...

Time Measuring technique. Stop form spam on the clock!

use Time measuring to stop form spam robots

Picture: bossfight.co

The spam robots are very fast and that's one of their features which they use to their advantage, but we can turn the things around using their speed to make them stand out and detect them.

In order to do that, I added a new field to our form. It has to be a hidden field and the value is the encrypted timestamp of the time when the form was loaded:

<?php require "encryptor.php"; ?>
<form action="backend.php" method="post">
    <input type="hidden" name="formLoaded6tY4bPYk" value="<?php echo Encryptor::encrypt(time()) ?>" />
    <label>Full name:</label>
    <input type="text" name="fullName" />
    <label>Email address:</label>
    <input type="email" name="emailAddress" />
    <label class="hide">Leave this field empty:</label>
    <input class="hide" type="text" name="phoneNumber6tY4bPYk" autocomplete="off" />
    <label>Your message</label>
    <textarea name="message" />
</form>
<?php
class Encryptor {
    static function encrypt($value) {
        $encryptedValue = base64_encode($value);
        return $encryptedValue; 
    }

    static function decrypt($value) {
        $decryptedValue = base64_decode($value);
        return $decryptedValue; 
    }
}

Of course, the code in the Encryptor class, as well as all the other code is only for demonstration purposes. Therefore, You have to implement a more secure encryption method. So, you can find a strong encryption algorithm in this blog post.

The validation

Finally, I pick up the backend.php file and add the validations for the time measuring technique:

...

require "encryptor.php";

define("MIN_TIME_TO_FILL_FORM", 5); // define the minimum time required to fill the form to 5 seconds

$honeyPot = $_POST['phoneNumber6tY4bPYk'];
$encryptedLoadedFormTime = $_POST['formLoaded6tY4bPYk'];

$loadedFormTime = Encryptor::decrypt($encryptedLoadedFormTime); // decrypt it
$formFilledInSeconds = time() - $loadedFormTime;

if (trim($honeyPot) != '') {
    // This is a spam robot. Take action!
    exit('Spam Bot detected by HoneyPot');
}

if(!isset($encryptedLoadedFormTime) || $formFilledInSeconds < MIN_TIME_TO_FILL_FORM) {
    // This is a spam robot. Take action!
    exit('Spam Bot detected by Time Measuring');
}

// It's a human. Continue with the rest of the validation.
...

As a result, I receive the parameter "formLoaded6tY4bPYk" (line 8) which has the encrypted timestamp when the form was loaded. I decrypt it (line 10), so I can use the current timestamp to know how long it took the user to fill the form (line 11).
Furthermore, I make sure I receive the value of the field, and that the user didn't spend less time than the minimum defined earlier (line 18).

Final considerations

  1. It's a good practice to log the spam attacks for further analysis. Especially the whole serialised request information.
  2. Also, consider adding the Spam Bot IP address to your blacklist or contribute with anti-spam projects such as https://www.projecthoneypot.org/
  3. When spam activity is detected, show a generic error message like "We're sorry, the form could not be sent at the moment. Please try gain later" rather than "SPAM detected".
  4. All the code presented in this article is only to illustrate the idea, not intended for production.

Picture with robots by epicantus.tumblr.com

FacebookTwitterPinterest

Antonio Valdez Arce

Senior Full Stack Developer

More than 10 years of experience in the IT industry has given me the opportunity to develop the ability to learn fast. From developing desktop applications to pushing pixels in a web site, this business demands the professional to keep updated and wider the range of specialisation, that's why I constantly try to stay in contact with new technologies and encourage my peers to do so.