Safer Contact Forms Without CAPTCHA’s
30 May 2006 | Tutorials | 45 Comments
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:
- The hidden tag is present
- The hidden tag’s value matches a ‘token’ stored as a cookie by your visitor’s browser
- The timestamp for the hidden tag hasn’t expired
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
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: jQuery, javascript, DOM, spam, spam prevention, contact form, Shiflett, AJAX, form processor
Subscribe
45 Comments
Leave a Reply
You can follow the discussion through the Comments feed. You can also pingback or trackback from your own site.






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?
On one of the last slides of the step by step tutorial I wrote:
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.
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
@glitsj,
Thanks for the heads up… that was hold over from a different version and I missed it. Extraneous js now gone… thanks.
This idea is bad. You know why? It requires Javascript to be enable. ’nuff said.
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.
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.
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.
I’m not sure I follow… can you connect the dots for me? I’d like to know what you’re suggesting.
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
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.
[…] 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. […]
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.
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!).
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
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!
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.
Great tutorial, but is this possible in asp.net. Would be great if someone can come up with one.
@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…
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.
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!!
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..
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
(continue) and then, send the cookie and the ts field together, validation in test.php seems to be useless absolutely.
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.
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
This is 12 timezones between us… My local time is 10:38
AM,24 May 2007
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.
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.
Put the code up as a .txt file so I can test it out and see if you get $50
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.
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.
Jack - so did the code by steampunk work?
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?
ok, we tried this on our forms , but after about a week people figured out a way around this.
greg,
I’ll email you with a request for more info.
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).
[…] 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 […]
[…] 12. Safer Contact Forms Without CAPTCHAs […]
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.
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…
[…] 12. Safer Contact Forms Without CAPTCHAs […]
[…] Safer Contact Forms Without CAPTCHA’s […]
[…] Safer Contact Forms Without CAPTCHA’sUse a hidden field in a form and don’t annoy your visitors with CAPTCHA. […]