Protecting Forms from Spam ‘Bots

Posted April 30th, 2008 by Mike Cherim

Spam robots or spam ‘bots abuse comment forms, contact forms, and any forms they can. A true scourge of the web. They exploit insecure forms to send spam. Fortunately there are individual methods of slowing them down. And when used jointly, ‘bots can actually be stopped. This post shares four scripted operations I’ve found particularly helpful to prevent illegitimate posting. In the following examples, at least one variable will be posted, and will appear in this article as $posted_var (meaning it’s captured by $_POST['var']) to offer greater clarity, but this variable can be whatever you need it to be: name, email, etc.

Also in this article, in the script blocks, I mention to Return an error or give value to a feedback/output variable. What you do depends on the overall build of your script. This can mean anything from using the echo() or die() functions to output a user error, or you can simply assign a value to a variable used to call an error from a database or flat file.

Here are the methods.

The Anti-Spam Question

This is one that lots of people are familiar with. In fact, thanks to my v2, v2WP, and v3 contact form scripts, the question “Is fire hot or cold?” is something found in the furthest corners of the web (though people should change it). The premise here is that spam ‘bots are essentially stupid, lacking all cognitive powers, so we can take advantage of their lack of humanity by asking a question only a human can answer. The question needs to appear in the label element associated with the answer input. It can and should be extremely simple. The answer to the question is the $posted_var and it must match the preassigned $answer. It’s made case-insensitive by the script.

Here’s the scripting:

<?php
    // To mitigate the possibility of user errors, eliminate case-matching issues
    $posted_var = strtolower($posted_var);
    $answer     = strtolower($answer);
    // If $posted_var doesn’t match $answer
if($posted_var != "$answer") {
    // Return an error or give value to a feedback/output variable
}
?>

Notes: This error can be human-triggered so it’ll be best to present helpful information in your error message. Don’t be rude, instead offer an alternative or even the proper answer. This must be a required field. Although there are other versions of this method, randomized questions, image-based (and voice recorded) methods, etc., this is the simplest, independent (not requiring a session) technique, and it is effective.

Menu Option Matching

If your form offers a pulldown menu (a select with option elements), making it a required field and applying option matching is really quite effective. The premise is that ‘bots don’t use a form the way human users will. They won’t chose one of the options offered. In most cases ‘bots will assign any value to the menu’s $posted_var that they can, and if it’s not spam, they’ll often resort to inputting the number ‘1’ just to satisfy the requirement to post something. That’s where this measure shines: with this script, the $posted_var must be a legitimate offering — assuming that is that you feed your options by way of an array(), not hardcoded.

Here’s the scripting:

<?php
    // If the $posted_var isn’t present as one of the options in the array
if(!in_array($posted_var, $menu_options)) {
    // Return an error or give value to a feedback/output variable
}
?>

Notes: Only ‘bots can trigger this error so you should be able to openly speak your mind in your error message, if you feel like being colorful in your speech. This must be a required field.

The Honeypot Spam Trap

When I devised these traps, they no doubt existed before I was clever enough to think of them on my own. This one is a classic example because months after I discovered its value, I found the technique by another name from Ned Batchelder in his article called Stopping spambots with hashes and honeypots. I immediately adopted the name. The premise is rather simple. You offer a hidden field (hidden by the offset class, positioning it with a large negative left margin) that should remain empty. Since ‘bots will fill everything and anything to circumvent any required field hiccup, they’ll fill this tempting input, too. Problem is, for them, the script doesn’t want it filled in and will return an error if it is. It’s important to actually warn of this in the label.

Here’s the scripting:

<?php
    // If the $honeypot (named so for clarity in this example) isn’t empty
if($honeypot != "") { // Could also be if(!empty($honeypot)) {
    // Return an error or give value to a feedback/output variable
}
?>

Notes: This error can be human-triggered so present helpful information in your error message. Don’t be rude, instead offer a method of clearing the input (most people will not be able to see it). Also, be sure to not identify this input with a typical name attribute because some autofill software will populate the field without the human user’s knowledge. Also, if using the offset class, send it left only so as to not torment keyboard users by sending them inexplicably back to the top. This must not be a required field.

Regulating Input Lengths

Many forms will limit the number of characters the user is able to provide in any given input. For example, you should be able to safely place a limit of about sixty characters inputted into a “name” field. This number is defined and physically limited by the use of the maxlength attribute in the input. But ‘bots, not using the form in the same way as humans, will think nothing of trying to enter 200 spam links in the name field. After all, their only objective is to send spam and don’t respect the input elements unless forced to. Because of this, it is essential that you support the physical limitations of the input elements by script-enforcing the regulation. The premise is simple, if ‘bots input more characters than what’s allowed for human users, the script won’t process and the form will spit out an error.

Here’s the scripting:

<?php
    // If the $posted_var is greater than sixty characters…
if(strlen($posted_var) > 60) {
    // Return an error or give value to a feedback/output variable
}
?>

Notes: Only ‘bots should be able to trigger this error so you can let ‘er rip, so to speak, in a colorfully-word error message, if that happens to bring you joy.

Are There Other Methods?

Yes, there are other worthy methods or variations of these methods — and there are also other security concerns, not just ‘bot posting. But these methods are my favorites. Solid, simple, and accessible. Some other methods limit accessibility. Others I have found problematic at times. Some, for example, will rely on server environmental or user variables, and that can lead to inconsistencies due to data deficiencies. I try to make my form scripts work under all conditions, but if building a contact form for your own site, you can get more creative in some areas.

That said, I have found the four methods I offered in this article, when used in concert, provide exceptional, unwavering ‘bot posting protection. So far so good, anyway.

Sufficient for Form Security?

In a nutshell, no! The focus of this article is specifically about keeping ‘bots from posting. There are other concerns such as any illegal remote posting, header injection exploits, open relay attacks, and other somewhat more human-associated threats one needs to be concerned with. In other words, employing the four techniques in this article is not enough to fully secure a form.

The more popular and well-known the site becomes in time, the greater the number and frequency of malicious attempts, from both humans and ‘bots. This is one of the more unfortunate aspects of the Internet. It’d be nice if we could leave the house without locking our doors, but it’s not that kind of neighborhood. We have to protect ourselves.


20 Responses to: “Protecting Forms from Spam ‘Bots”

  1. James Dimick responds:
    Posted: April 30th, 2008 at 11:56 am

    Great article! All very good methods. Thanks for sharing your knowledge on the subject. Can never have too much protection from spam…

    Why isn’t spam punishable by death? :P

  2. Jared Smith responds:
    Posted: April 30th, 2008 at 2:28 pm

    As you note, there are MANY methods of eliminating (or at least reducing) bot spam that have minimal or no impact on users and accessibility. All of your methods do have potential, though negligible, impact on users. I’ve documented several additional ways to block bots using scripting alone at http://www.webaim.org/blog/spam_free_accessible_forms/.

    I find that the implementation of inaccessible CAPTCHAs could be avoided the vast majority of the time if just a few of these methods are implemented.

  3. Anthony Brewitt responds:
    Posted: May 7th, 2008 at 10:46 am

    I think Mike is right in that the they do have zero impact on accessibility and also that these methods are both simple to implement and as always “Accessible”, Nice collection and yes I do hold you responsible for the hot or cold questions we all have to fill in now to leave a comment :-) One thing that does worry me is the amount of “mail to” links you still see around company websites. If you want spam that will give you infinite amounts - if you must do this use an image or better still use a hot or cold question.

  4. Anthony Brewitt responds:
    Posted: June 13th, 2008 at 6:10 am

    I have seen some really bad anti spam methods including the popular captcha anti span methods; I remember one where you have to count the cats over the dogs, you have to click on a wav file to hear the numbers and most show a horrible distorted image that I cannot read and I have perfect vision, truly unaccessible and unusable.

  5. Chris Stewart responds:
    Posted: June 30th, 2008 at 1:21 pm

    Have you ever thought of doing up a coldfusion version of this anti-spam method? That would be seriously cool.

  6. Chris Stewart responds:
    Posted: July 1st, 2008 at 7:36 am

    I put something together yesterday in CF based on the ideas I read in this article. Let me sum up what happens here and tell me if I am right. I have a a form that contains the text box and question is it hot or is it cold. I pass whatever is entered as an answer to the page that processes the form and I check to see if that form field is anything other than ‘hot’. If it is hot I run the SQl to insert the comment into the DB. If not I display the error message and stop all further actions.

    Is there anything else happening behind the scenes that isn’t covered in this article?

  7. Chris Stewart responds:
    Posted: July 1st, 2008 at 8:44 am

    Yes that is what I did. If the answer is answered incorrectly I stop all processing and display an error message. I will look into some of the other security additions you mentioned.

    Thanks a lot! Great site. Very interesting articles.

  8. العاب responds:
    Posted: August 5th, 2008 at 7:59 am

    This number is defined and physically limited by the use of the maxlength attribute in the input

  9. John responds:
    Posted: August 26th, 2008 at 11:22 am

    Great ideas! I’m new to php applications, but instead of writing your error in the php validation
    “Only ‘bots should be able to trigger this error so you can let ‘er rip, so to speak, in a colorfully-word error message, if that happens to bring you joy”
    Perhaps its possible to send them off to remote parts of the web using {header(”Location:http://xyz.pdq”);exit();} , rather than let them try again to have a successful form entry. I suppose the bots can hit the back button…. a never ending loop of headers might tie them up as well.

  10. John responds:
    Posted: August 26th, 2008 at 12:01 pm

    ” never ending loop of headers might tie them up as well..
    ..testing on my server ie 6 goes into endless loop, firefox does not just gives message, ie7 goes through one round then errors. Seems best to forward to a known site or loop of bogus forms.

  11. SunnyFrimley responds:
    Posted: September 18th, 2008 at 4:01 am

    Doesn’t that honeypot approach cause problems for people with screen readers? I only ask because you seem keen on accessibility.

  12. Brian Moreau responds:
    Posted: February 11th, 2009 at 5:22 pm

    Ok I have a few sites with web forms and most from time to time have suffered from spam bot attacks.
    I assume the bots somehow edit the $to field even if it is preset in your mail function in php thus.

    $toEmail = “youremailaddress@yourdomain.com”;
    mail ($toEmail, $Subject, $message, $headers);

    I wonder if anyone can explain how this is done?
    In the mean time I have adopted the honeypot method and the trap is a hidden field as I did not want any extra fields on display.

    I have written my checking script as below and if an error is set the mail wont send.
    This is assuming the spam bot will attempt to fill in the hidden field.

    if ($name != “”) {
    # this is a honeypot trap
    $error = “SPAMMER BOT”;
    }

    Hope this works.

Sorry. Comments are closed.




Note: This is the end of the usable page. The image(s) below are preloaded for performance only.