In a discussion on Scratch Forums on the merits of CAPTCHA, one of our members suggested a possible alternative that detected JavaScript events (mouse events, keystrokes) to show that it's a human completing the form.
I took on the challenge of creating a simple JS-based alternative to CAPTCHA, which I'll explain below.
High-level Requirements
- The form should record certain events to prove human involvement.
- If JavaScript is not enabled, there needs to be a fallback solution.
- The tests should be invisible to the user where possible.
- It should not be possible for a bot to discover the test and to spoof the results.
Am I Human?
First of all, our form will need some way of sending the answer to "am I human" through to the back-end script.
For this, I'll just use a hidden field. Note, following requirement 4 above, this field could be named anything. If bots learn the likely name for this field, they could send the correct value through and bypass the system.
I'll put the hidden field before the closing form tag (but you could put it anywhere in the form).
The value of this field is zero by default, which means "no - not a human".
Switch it For Humans
We want to set the value of "imahuman" (or your own name, please) when certain events take place, which we'll detect using Javascript.
Following best practice, the function that sets up the event detection should be called by an event handler, and that event handler should be attached using an external Javascript file, not written into your HTML code. Like this…
What's happening here (working from bottom upwards):
- The addEvent stuff at the bottom means you can simply call a single addEvent(what element, what event, function you want to run, escalation) command in almost any browser.
- The function markAsHuman will be called when we think we have a human. It simply changes the value of the hidden field from zero to 1.
- Note, it might be sensible to change these values to something less obvious.
- function setUpHumanTest() actually runs through every form on the page (why not?) and attaches 2 javascript event handlers to the form:
- If the form receives the focus, we fire the markAsHuman() function;
- Same if the form receives a click.
- Both these events only happen in the browser window, so a code/spambot wouldn't be able to reproduce them.
- Finally, at the very top, the addEvent() call tells the browser to run the setUpHumanTest() function when the page has loaded.
Testing Results with Script
Your form will submit to a middleware script (PHP, ASP etc.) You'll simply need to tell this script to reject any submissions that still have "imahuman" (or equivalent) set to zero (or whatever you use).
Personally, I like to pretend that the script has worked, and forward the bot to the success page, but not do whatever is supposed to happen with a valid input. I figure that there's no reason to tell a bot that you've twigged its game and draw attention to your site.
What About When JavaScript is Off?
Clearly, we need to handle the (approximately 10%) minority that don't use JS. This now includes some mobile browsers and text-to-speech readers, not just people exercising their choice for a diminshed browsing experience.
It's very easy to show extra special HTML when JS is not working: you just use the <noscript></noscript> tag.
Here's what I do:
This is really simple, but you could replace it with a CAPTCHA test if you prefer.
The field only allows a one-character response, so you have to type "7". This is also easy to change.
The back-end script would then need to test first whether "imahuman" is set "on", OR if the alternative human check is correct. If either is OK, we're OK.
I hope you find this technique easy to implement and useful on your own sites. Please post any improvements to the forum thread.
(Note, we usually use definition lists for form layout these days. Not the strictest semantic application of the tag type, but I think it's OK, in the broader application of name/value pairs.)