Safer Contact Forms Without CAPTCHA’s

30 May 2006 | Tutorials | 45 Comments

From the creator of "15 Days of jQuery" (me):

Here’s a subject that’s close to my heart: secure contact forms.

As I mentioned in a previous tutorial, one common use of contact forms is to help visitors communicate with you without exposing your email address to the email harvesting software used by spammers.

But when it comes to spam, hardly anything’s worse than an insecure contact form. Imagine getting a nasty note from your web hosting company that your site has been used to send out massive amounts of email about black market erection medication and, oh by the way, your site is offline until you get it fixed - thank you very much.

So what I’m going to show you today is a simple method for adding an extra layer of security to ANY contact form on your site - even if you don’t use my super secure and wonderfully flexible Ultimate Form Mail.

The Situation

You notice that spammers have been remotely probing your contact forms for vulnerabilities… and you want them to quit.

The Problem

You don’t want to use CAPTCHA’s (Completely Automated Public Turing Test to Tell Computers and Humans Apart) because you just know that requiring your visitors to read squiggly letters and numbers just to send you a message is going to suppress communication - not encourage it.

Bottom line: you want to make life tough for the Bad Guys and super simple for the Good Guys.

The Solution

You’re going to use jQuery to add some hidden tag information to the contact form when the page loads. When the form is sent to the processor, you’ll use some simple PHP code to verify the following:

In other words, your visitor will have a limited amount of time (specified by you) to fill in the form and send it. And if a spammer tries to post information to your form processor remotely they’re going to hit a big fat roadblock. Do not pass go, do not collect $200.

What I’m going to share with you is a modified concept I read from a very smart fellow named Chris Shiflett. He’s a security expert on all sorts of issues that PHP programmers can come across if they’re not careful.

The Tutorial

I got great responses from the tutorial for Table Striping Made Easy so I’ve decided to do another “walk through” tutorial with screen grabs. It’s a little time consuming, but hey, you’re worth it.

Tutorial on Safer Contact Forms Without CAPTCHA’s

Demo

Code

Silver Bullet?

“So now my forms are 100% secure and I can use Generic Free Contact Form Processor With Sloppy Code and feel safe?”

Uh… no.

This security concept is based on a key assumption:

Spammers would prefer to go after the “low hanging fruit” than spend all day trying to crack a tricky contact form.

Now listen carefully, dear friend:

This technique, although strong, is not a cure for a weak form processor.

My attitude towards securing a contact form is to use multiple methods on both the server side and the client side so that a spammer is going to have to invest an enormous amount of resources to even come close to succeeding with their evil plot.

I view the client side protection as analogous to posting a sticker on the windows of a home to indicate that the house is wired with an alarm. Thieves know to look for stickers, dogs in the yard, lights on the exterior of a home, and other signs of a well guarded house. They’re looking for high payoff with minimal work and risk.

In other words, if you can thwart 99% of attacks before they really get started and you can do it so easily why wouldn’t you? That’s what this technique does.

But it’s not a silver bullet cure for a crappy form processor.

Technorati Tags: , , , , , , , ,

Share and Enjoy:These icons link to social bookmarking sites where readers can share and discover new web pages.

Subscribe

Email Updates:
Email:
RSS Feed:

RSS information | Email Policy | RSS to Email by Aweber

45 Comments

  1. Dave Cardwell said on 31 May 2006 at 3:46 am:

    Am I right in thinking you would not allow contact through the form unless the jQuery values had been set?

    Where would this leave people with JavaScript disabled, and users of assistive technologies without the option?

  2. Jack said on 31 May 2006 at 9:02 am:

    On one of the last slides of the step by step tutorial I wrote:

    Con: a teeny tiny percentage of your audience will either have javascript or cookies disabled

    You just have to weigh this against your alternatives:

    * Less security for your forms + spammers probing for weak points
    * Annoying CAPTCHA tests that suppress communication with visitors

    In addition, in the demo, I wrote about a warning message that could be shown if javascript is disabled. You could put a link in this message to a different form, or other options for contact.

    My tutorial provides a possible solution with lots of upside but with certain downside.

    It’s up to you to decide if the benefits outweigh the costs.

  3. glitsj said on 31 May 2006 at 10:52 am:

    Thx for your continuous work to make forms safer and manageable !

    Just one small remark. Your demo.php file also references cookieFunctions.js, which doesn’t seem to be either needed nor included …

    Keep those tutorials coming :D

  4. Jack said on 31 May 2006 at 12:06 pm:

    @glitsj,
    Thanks for the heads up… that was hold over from a different version and I missed it. Extraneous js now gone… thanks.

  5. Alexander K. Schrøder said on 17 Jul 2006 at 3:29 am:

    This idea is bad. You know why? It requires Javascript to be enable. ’nuff said.

  6. Jack said on 17 Jul 2006 at 10:34 am:

    Alexander,
    Labeling a technique, such as this, as “bad” or “good” is simplistic and assumes that you know the “right” way.

    This is a technique.

    It has benefits - and it has drawbacks.

    As others have commented, it can be useful for some developers. For you, it is of no use.

    ’nuff said.

  7. Gilles said on 13 Oct 2006 at 9:40 am:

    You won’t even need cookies for this technique. In the PHP file that generates the hash, you could set a session variable. In the PHP file validating the input, you could dynamicly check for the input tag which should have been generated by jQuery to see if the user had javascript enabled.

  8. Tarwin said on 28 Nov 2006 at 2:01 am:

    There was an interesting discussion on this topic on Humanized.org weblog.

    I proposed something similar to this in a comment, but after reading this, and being impressed, realized that you could add another layer to make it totally non JS reliant.

    Have a CAPTCHA in the form which is removed by JS. This way works with JS off.

  9. Jack said on 29 Nov 2006 at 1:32 pm:

    I’m not sure I follow… can you connect the dots for me? I’d like to know what you’re suggesting.

  10. Deep said on 5 Dec 2006 at 6:51 am:

    Good stuff, one thing noticed in your code, it is calling for a js file for cookie (cookieFunctions.js) but that file isnt present in the archive.

    Regards,
    Deep

  11. Jack said on 6 Dec 2006 at 9:24 am:

    Deep,
    I checked my demo. The files I packaged up must have a line of code with a hold over from development. Please ignore the reference to the javascript file.

  12. Design to CSS plus Ajax contact form, etc. | The Montoya Herald said on 5 Jan 2007 at 12:52 am:

    […] The final product combined some PHP code with jQuery and some code from the jQuery forms plugin, and the whole lot wouldn’t have been possible without Safer Contact Forms Without CAPTCHA’s and Form Submission ajaxSubmit(). The final solution is not bad. When Javascript is not available, a message sharing my e-mail address is visible so people can still contact me. With Javascript available, the form appears as normal and upon submission, it returns responses based on whether the spam check passed and whether all fields were filled in. If it’s successful, a thank you message appears and the form clears. It’s all right. I’m glad I got it done. […]

  13. JsD said on 6 Jan 2007 at 7:40 pm:

    This technique is also very handy for login pages to prevent spoofing. I developed such a page a few years ago and worked like a charm.

  14. Corey said on 18 Feb 2007 at 1:28 am:

    Eh, maybe I’m alone here, but this wouldn’t be too hard to crack…assuming I understand what you explained.

    All a remote script needs to do is retrieve the page, then request the hash and send along the cookie as it does it….then submit the form. Sure, it’s a little bit more work, but not more than 10 minutes.

    Anything a browser can do without the user interacting a bot can do without a user. I think you understand this since you talk about it just making it harder, but I think you overestimated the level of security this will provide.

    It’s a great idea though. I came up with something similar a few years ago…I never implemented it though since I haven’t needed it (lucky me!).

  15. BillsGate said on 6 Apr 2007 at 4:53 am:

    This solution is simple, but brilliant.

    Although it is not quite the holy grail, it’s an easy way to separate visitors from bots.

    Thank you very much for this example!

    A sidenote though, which has nothing really to do with captchas: a common pain in the * is the use of mail forms on websites by bots, to send masses of spam from your server.
    If the bot wants to succeed, he has to put a header (mime-type) into the textarea.
    Let PHP filter this:
    $tmpstr = implode(”",$_POST);
    if(strpos(”mime-type”,$tmpstr)===true){
    exit(”Your message is considered spam”);
    }

    That, combining with this JQuery addon, should be quite waterproof I think. Just my two cents :)

  16. Elliot said on 6 Apr 2007 at 6:08 pm:

    Thanks for 15 days. I’m a jQuery beginner and am getting a lot out of your series. In addition to the substance, I love the slide show, especially those beautiful colorized code cutout images. Would it be rude to ask what tools you use to produce those? If so, my apologies. If not, I’d be appreciative. Thanks!

  17. Jack said on 9 Apr 2007 at 10:35 am:

    Elliot,
    Sure. I use a Techsmith product for screengrabs called SnagIt. I bought this as a bundle with another product, and I betcha you can find much cheaper ways to reproduce my cutouts. But that’s what I use, because I have it and it’s pretty easy.

    As for the slideshow, it’s a javascript you can download free. I believe if you look at the javascript file of the slideshow you will see a url to the author’s website.

  18. JO said on 18 Apr 2007 at 9:07 pm:

    Great tutorial, but is this possible in asp.net. Would be great if someone can come up with one.

  19. Aaron said on 21 Apr 2007 at 1:21 pm:

    @JO,
    I recently had to reduce some comment spam, and wrote about it here: http://intrepidnoodle.com/blog/show/9.aspx

    Its a bit simpler than this technique, and I suspect probably doesn’t work as well, but its so easy to implement that its worthwhile to stay out of the ‘low hanging fruit’ example…

  20. Mike Robinson said on 26 Apr 2007 at 12:11 pm:

    A technique that I have used with some success is based on the realization that spam-bots are either ‘extremely fast’ or (by using previously-cached strings) ‘extremely slow.’

    When the input-form is generated, it is marked “do not cache” (so the browser will actually retrieve something new from us), and it includes a hidden-field containing an unrecognizable hash.

    This hash is based on the IP-address of the requester, a salt-value and a garbage string known only to us. It masks, by means of exclusive-OR, the server date/time and another checksum/hash.

    When we receive the input, we unmask it, verify the checksum in the unmasked data (to know that the unmasking worked), then check the timestamp thus revealed.

    This timestamp must be (say) more than 30 seconds old, and not more than 15 minutes old.

    Typically, spam-bots are in a hurry. This stops them dead.

  21. Elliot said on 27 Apr 2007 at 4:08 pm:

    Jack, thanks very much for the tips on Techsmith’s SnagIT, and DOMslides for screen shots and html slide show. Very helpful. And again, you’ve been very generous with 15 days. Great work and much appreciated!!

  22. marconi said on 10 May 2007 at 11:19 pm:

    the tutorial is cool but it validates even if the name is empty so i added

    !empty($_POST[’name’])

    in the first condition and works great now..

  23. Rodin said on 22 May 2007 at 11:03 pm:

    Sorry for saying this method is poor…I’ve worked on attacking CAPTCHAs, in my way , first step of analysis a CAPTCHA is watching the post\get data and cookies. Putting the tokens or keys to the client side is not a good idea - can be easily captured, and this

  24. Rodin said on 22 May 2007 at 11:49 pm:

    (continue) and then, send the cookie and the ts field together, validation in test.php seems to be useless absolutely.

  25. Jack said on 23 May 2007 at 1:35 pm:

    Rodin, and anyone else who says this method is poor,

    I don’t mind being wrong.

    But there are four things that tell me these kinds of replies are silly:

    1) Adding similar code dramatically reduces automated spam attempts on real life websites over and over again. Based on actual experience, it works quite well.

    2) From what I gather, the comments about how easy this is to beat talk about the *specific* demo I’ve put up… But tweak the name of the hidden text field, the name of the cookie, and some of the validation, and your bot code is SOL.

    3) I would love to see some php or cgi that grabs form fields that are generated through DOM, as jquery would do. Not saying it can’t be done, just haven’t come across it yet. I’m sure it exists, so show me.

    4) This isn’t about creating a form that is unbeatable… I clearly say that in my post. It’s about thwarting automated spam. If the technique eliminates 99% of most automated spam attempts before it begins, does it have any worth? I think so.

    If you, or someone else reading this post, can create the code I’m told is so easy to create but I haven’t seen yet, I’ll put my money where my mouth is.

    Show me the code that will remotely:
    1) read all cookies set, regardless of cookie name, even session variables
    2) read all form fields, regardless of name, including fields that are added after page load using javascript DOM methods

    I’ll pay the first person to show me such code $50 if it meets those specific criteria.

    Not saying it can’t be done…

    But show me.

  26. Rodin said on 23 May 2007 at 10:28 pm:

    Jack,thank for your reply, and sorry for my extremely words before, and also my poor English, maybe those words make you feel not good, but plz believe me, i have no malice…

    1) I agree with that it can works. Actually in my opinion, main reason is not spammers cannot work out this, but because this method havn’t applied commonly, they do not want waste time on such few sites.
    2) If this method is used in a widely used open source web application, most of users are lazy or unable to
    change the code, except using a builtin function. So tweak the name maybe not as effective as considered.
    3) CGI may unable to do those, but the desktop application can work out.
    4) Generally speaking, i like this quick and easy method, and will use it in my personal site.

    I have currently no way to run the JS within a bot, and no common way to defeat most of CAPTCHAs. On the other hand, write a specific spam bot is much easier.
    1) all cookies set can be read at HTTP response header: Set-Cookie . Session variables cannot be read , but in MS ASP(i’m a ASP programmer) server-IIS, session is implemented with a cookie like ‘ASPSESSIONIDSDKFJSFS=KSDJFKLSDFSDFSF’ (this string length maybe not wrong, just show the pattern), sending the same ASPSessionID, IIS will process requests as same user.

    2) This criteria seems hard to me - I’ll wrote a script-supported browser if i can do this. It’s also difficult/complex to automaticly read the form fields name. Easier implementation is watch out a valid user operation, capture all the request data, then simulate that in bot.

    If you really want a bot, I’ll write one working but may not meets the criteria.

    At last, I still interest at the $50 - it’s almost equals my monthly living cost :-(

  27. Rodin said on 23 May 2007 at 10:39 pm:

    This is 12 timezones between us… My local time is 10:38 :-)

  28. Rodin said on 23 May 2007 at 10:39 pm:

    AM,24 May 2007

  29. Jack said on 24 May 2007 at 10:33 am:

    I need to move where you live!

    Anyhow, thanks for your replies.

    Ideally, I’d be interested in a PHP script that did what I’ve laid out.

    And I agree… it would be very difficult.

    Your suggestion of a browser type application is the one that I see as most likely for doing everything I’ve requested for the “prize” money.

    That said, this reiterates what I’ve said before: this isn’t the “end all be all” of thwarting automated spam, but in practice it knocks out almost all of it.

  30. steampunk said on 4 Jun 2007 at 7:07 pm:

    curl token.php to get the page with cookies[token], build ts variable from response, use it
    and the form post/get variables with curl again which results in
    (isset($_POST[’ts’]) && isset($_COOKIE[’token’]) && $_COOKIE[’token’] ) == md5(’secret salt’.$_POST[’ts’])) hence
    $proceed = true;
    kill curl , repeat.
    if you put just this looped into a php file and run it as a shell scripts then you can you change your ip on your network repeatedly.

    and just to be annoying, firefox = open source, firefox = web platform capable of doing the above as a plug in. you do this all the time with you use your fancy video grab plug ins or seo junk…

    come to think of it , a ff/ie plugin that inserts a javascript to grab the form variable then submit and clear/alter cookies. reload page repeat.

    conclusion, people could sent bogus form information and spam the world using this method.

  31. Jack said on 5 Jun 2007 at 9:03 am:

    Put the code up as a .txt file so I can test it out and see if you get $50

  32. Tobbe said on 7 Jun 2007 at 6:51 am:

    I think I will use this in combination with a captcha. I’ll have some JS remove the captcha so it isn’t seen by most people, and then on the php side I first look for the hidden tag. If it matches the token I go ahead with the form processing. If it doesn’t I check the captcha which should have been visible in case of JS being disabled on the client side.

  33. phpdude said on 26 Jun 2007 at 3:35 pm:

    If you use a server side scripting language, why could you not embed a unix timestamp and an md5 hash for it as hidden form fields when the form page 1st loads.

    Then when you get a post you would first look to be sure the timestamp is within your pre-determined timeframe and that its md5 hash is correct.

    If so, go with it. If not, drop it on its head.

  34. vermontdevil said on 26 Oct 2007 at 2:16 pm:

    Jack - so did the code by steampunk work?

  35. Paul Koppen said on 27 Oct 2007 at 6:21 am:

    First I would like to say, I am really surprised by the simplicity of this method and the fact that *all* php is done in the token file. Truly clever.

    But then, of course, I have some comments.
    1. I assume you set the cookie instead of a session to keep your solution stateless? Because storing it in a session variable would be just as easy and theoratically more secure (in your solution, an attacker can brute force the salt, and yes I read your 1000 comments on silver bullets).

    2. As Mike Robinson put forward, typical bot behaviour can be characterized by either slow or fast responses. So putting a lowerbound on the timestamp is really a good idea.

    3. The comment by Tarwin (instead of the warning message, remove a CAPTCHA) is extremely good, for multiple reasons: a) With javascript disabled, the form is still accessible. b) Bots see the CAPTCHA and leave. c) If a bot is so extremely clever to solve the CAPTCHA and your system, we can catch ‘em because they send back two solved challenges! d) It is gracefully downgrading; Annoying CAPTCHA tests now suppress only communication with annoying visitors :)

    4. It would be friendly to the visitor to tell him/her that the submission time has expired and provide a way to reload without losing form content, wouldn’t it?

  36. greg said on 16 Nov 2007 at 7:57 am:

    ok, we tried this on our forms , but after about a week people figured out a way around this. :(

  37. Jack said on 16 Nov 2007 at 4:10 pm:

    greg,
    I’ll email you with a request for more info.

  38. Chris Wash said on 28 Nov 2007 at 7:25 pm:

    This strategy is known to the Java world as the Synchronizer Token pattern. I didn’t see you explicitly point this out, but what its main purpose in life is for is to make sure that redundant posts are not accepted - i.e., you can only submit one form-per-token that you get from the server.

    This helps ensure a client’s posts are synchronized with a new transaction on the server every time (consider submitting a payment) - hence the name.

    Many Java web frameworks implement this sort of thing for you out of the box - skipping the JQuery step of inserting the hidden form field in dynamically and just automatically including the hidden field in the form for you. This method requires no Javascript, and you can suggest it as a way to deal with clients that don’t have Javascript. The Javascript applies just an added level of obfuscation to the whole process and isn’t really needed to achieve what you’re looking for.

    Another similar pattern to take a look at in terms of secure form processing is PRG (http://en.wikipedia.org/wiki/Post/Redirect/Get).

  39. Null Reference » CAPTCHA My Cat said on 14 May 2008 at 2:54 pm:

    […] are various alternate solutions being banded around such as Client Side Javascript CAPTCHAs and verbal reasoning (via 37Signals) tests. The oddest so far has to be the image based effort by […]

  40. 25 Excellent Ajax Techniques and Examples - Six Revisions said on 2 Jun 2008 at 11:58 pm:

    […] 12. Safer Contact Forms Without CAPTCHAs […]

  41. Jeff said on 3 Jun 2008 at 2:50 pm:

    By saying this is bad because it requires javascript to be enabled is like saying, captcha is bad because it requires images to be enabled. Javascript is a fundemental part of the web these days, and usually the ones with javascript disabled would be bots or hack attempts which shouldn’t be accessing the site anyways. There are times when I have used linux based text browsers, but that’s only if I’m downloading files. I say, if they don’t have javascript enabled, too bad.

  42. gabe said on 4 Jun 2008 at 10:58 pm:

    JS runtimes: Rhino, Spidermonkey etc. could be used to eval the JS. Eg: appjet.com, XULRunner, Helma etc.

    Implementing the browser environment is a bit hard though even with the JS runtimes so you could get away with this for a while. Though a simple XULRunner or Firefox Extension app would have the environment to run anything in FF.

    I don’t think I’m understandint this fully though. Are you just setting a cookie when the XHR is made, or are you sending back JS that needs to be interpreted. If its justa cookie, then this isn’t JS dependent, if its obfuscated JS, then yes the spammer would need a JS interpreter or some nifty regex. If you implement some browser specific environment features, then you could get even further as the spammer would need to implement those in their bot. Could get expensive enough you might have something for the masses… :)

  43. Reponere » Blog Archive » 25 Excellent Ajax Techniques and Examples said on 6 Jun 2008 at 2:01 pm:

    […] 12. Safer Contact Forms Without CAPTCHAs […]

  44. Safer Contact Forms Without CAPTCHAs | Webmaster-Source said on 10 Jun 2008 at 7:17 am:

    […] Safer Contact Forms Without CAPTCHA’s […]

  45. “The Complete Guide” for jQuery Developer- Reblog « Dynamic Disruption said on 18 Aug 2008 at 10:43 pm:

    […] Safer Contact Forms Without CAPTCHA’sUse a hidden field in a form and don’t annoy your visitors with CAPTCHA. […]

Leave a Reply

You can follow the discussion through the Comments feed. You can also pingback or trackback from your own site.