Quick and dirty CAPTCHA Guide – for ColdFusion 8

A few months ago I posted a quick guide to walk folks through adding CAPTCHA’s to forms:

Quick and dirty CAPTCHA Guide

This guide made use of the excellent Lyla CAPTCHA component. One of the new features of ColdFusion 8 is a built in CAPTCHA generator. So let’s take a look at how we can do it the CF8 way…

First off, let’s start with a simple contact us style form. I won’t go into details about this form. It’s a basic self-posting form with validation for a name and comment box.


<cfset showForm = true>
<cfparam name="form.name" default="">
<cfparam name="form.comments" default="">

<cfif isDefined("form.send")>
<cfset errors = "">

<cfif not len(trim(form.name))>
<cfset errors = errors & "You must include your name.<br />">
</cfif>

<cfif not len(trim(form.comments))>
<cfset errors = errors & "You must include your comments.<br />">
</cfif>

<cfif errors is "">
<!--- do something here --->
<cfset showForm = false>
</cfif>

</cfif>

<cfif showForm>

<cfoutput>
<p>
Please fill the form below.
</p>

<cfif isDefined("errors")>
<p>
<b>Correct these errors:<br />#errors#</b>
</p>
</cfif>

<form action="#cgi.script_name#" method="post" >
<table>
<tr>
<td>Name:</td>
<td><input name="name" type="text" value="#form.name#"></td>
</tr>
<tr>
<td>Comments:</td>
<td><textarea name="comments">#form.comments#</textarea></td>
</tr>
<tr>
<td> </td>
<td><input type="submit" name="send" value="Send Comments"></td>
</tr>
</table>
</form>
</cfoutput>

<cfelse>

<cfoutput>
<p>
Thank you for submitting your information, #form.name#. We really do care
about your comments. Seriously. We care a lot.
</p>
</cfoutput>

</cfif>

Hopefully nothing above is new to you. So lets start updating this with some CAPTCHA love. First off, creating a CAPTCHA in ColdFusion 8 is incredibly easy. It takes all of one tag:


<cfimage action="captcha" width="300" height="75" text="paris">

The width and height determine the size of the image. The text determines what text will be displayed on the CAPTCHA. You can also determine what fonts to use – as well as the difficulty level.

So that part is easy. Everything after that takes a little bit of work. The first thing you need to figure out is what text to use. In the example above I used a hard coded value, paris, but in the real world you wouldn’t do that. If you do, spammers would get past your CAPTCHA rather quickly.

You can create a list of random words – but unless your list is pretty big, you will again have the issue of spammers being able to guess the word. Instead, I recommend a random set of letters. I’ve built a UDF just for this purpose. Let’s take a look:


<cffunction name="makeRandomString" returnType="string" output="false">
<cfset var chars = "23456789ABCDEFGHJKMNPQRS">
<cfset var length = randRange(4,7)>
<cfset var result = "">
<cfset var i = "">
<cfset var char = "">

<cfscript>
for(i=1; i <= length; i++) {
char = mid(chars, randRange(1, len(chars)),1);
result&=char;
}
</cfscript>

<cfreturn result>
</cffunction>

This UDF simply creates a random string from 4 to 7 characters long. You can tweak that size all you want, but any more than 7 will probably tick off your visitors. Also note the range of characters. I removed things like 1 (number one), l (lower case ‘el’), and I (upper case “eye’) since they can be confusing. Thanks to the NYCFUG members for feedback on this.

So once we have the UDF, we can now generate random text. But now we have another problem. When we submit the form, we are going to need to validate that the text you entered is the same as the text in the image. To do that, we need to store the text. Imagine if we did this:


<cfset captcha = makeRandomString()>
<input type="hidden" name="captchatext" value="#captcha#">

As you can imagine, this is not very secure. A spammer would simply look for the hidden form field. So we need to encrypt the string somehow. ColdFusion offers multiple ways of doing this. For example though I’ll just hash it:


<cfset captcha = makeRandomString()>
<cfset captchaHash = hash(captcha)>

Then I can add the CAPTCHA to my form like so:


<tr>
<td>Enter Text Below:</td>
<td><input type="text" name="captcha"></td>
</tr>
<tr>
<td colspan="2">
<cfimage action="captcha" width="300" height="75" text="#captcha#">
<input type="hidden" name="captchaHash" value="#captchaHash#">
</td>
</tr>

Now the form has both the captcha and the text in hashed form. The last step is to just add the new validation. I do this by hashing the user’s text against the hidden form field:


<cfif hash(ucase(form.captcha)) neq form.captchaHash>
<cfset errors = errors & "You did not enter the right text. Are you a spammer?<br />">
</cfif>

And that’s it. I’m done. The complete template is below. Enjoy.


<cffunction name="makeRandomString" returnType="string" output="false">
<cfset var chars = "23456789ABCDEFGHJKMNPQRS">
<cfset var length = randRange(4,7)>
<cfset var result = "">
<cfset var i = "">
<cfset var char = "">

<cfscript>
for(i=1; i <= length; i++) {
char = mid(chars, randRange(1, len(chars)),1);
result&=char;
}
</cfscript>

<cfreturn result>
</cffunction>

<cfset showForm = true>
<cfparam name="form.name" default="">
<cfparam name="form.comments" default="">
<cfparam name="form.captcha" default="">
<cfparam name="form.captchaHash" default="">

<cfif isDefined("form.send")>
<cfset errors = "">

<cfif not len(trim(form.name))>
<cfset errors = errors & "You must include your name.<br />">
</cfif>

<cfif not len(trim(form.comments))>
<cfset errors = errors & "You must include your comments.<br />">
</cfif>

<cfif hash(ucase(form.captcha)) neq form.captchaHash>
<cfset errors = errors & "You did not enter the right text. Are you a spammer?<br />">
</cfif>

<cfif errors is "">
<!--- do something here --->
<cfset showForm = false>
</cfif>

</cfif>

<cfif showForm>

<cfset captcha = makeRandomString()>
<cfset captchaHash = hash(captcha)>

<cfoutput>
<p>
Please fill the form below.
</p>

<cfif isDefined("errors")>
<p>
<b>Correct these errors:<br />#errors#</b>
</p>
</cfif>

<form action="#cgi.script_name#" method="post" >
<table>
<tr>
<td>Name:</td>
<td><input name="name" type="text" value="#form.name#"></td>
</tr>
<tr>
<td>Comments:</td>
<td><textarea name="comments">#form.comments#</textarea></td>
</tr>
<tr>
<td>Enter Text Below:</td>
<td><input type="text" name="captcha"></td>
</tr>
<tr>
<td colspan="2">
<cfimage action="captcha" width="300" height="75" text="#captcha#">
<input type="hidden" name="captchaHash" value="#captchaHash#">
</td>
</tr>
<tr>
<td> </td>
<td><input type="submit" name="send" value="Send Comments"></td>
</tr>
</table>
</form>
</cfoutput>

<cfelse>

<cfoutput>
<p>
Thank you for submitting your information, #form.name#. We really do care
about your comments. Seriously. We care a lot.
</p>
</cfoutput>

</cfif>