A reader recently sent me a note saying he was trying to add CAPTCHA to his site. He had been trying to see how I used it in BlogCFC, and was just confused by what he saw. I thought I'd write a quick and simple guide for getting CAPTCHA on a form.
First - let's look at a simple form without CAPTCHA.
<cfparam name="form.name" default="">
<cfparam name="form.email" default="">
<cfparam name="form.comments" default="">
<cfset showForm = true>
<cfif structKeyExists(form, "sendcomments")> <cfset error = ""> <cfif not len(trim(form.name))> <cfset error = error & "You must include your name, bozo.<br>"> </cfif> <cfif not len(trim(form.email)) or not isValid("email", form.email)> <cfset error = error & "Include a valid email address idiot!<br>"> </cfif> <cfif not len(trim(form.comments))> <cfset error = error & "It's called a Comment Form, stupid.<br>"> </cfif> <cfif error is ""> <cfmail to="foo@foo.com" from="#form.email#" subject="Pointless comments from the public" wraptext="75"> From: #form.name# (#form.email#) Comments: #form.comments# </cfmail> <cfset showForm = false> </cfif> </cfif>
<cfif showForm> <cfif structKeyExists(variables, "error")> <cfoutput> <p> <b>Please correct these errors:<br> #error# </b> </p> </cfoutput> </cfif>
<cfoutput> <form action="test.cfm" method="post"> <table> <tr> <td>Your Name:</td> <td><input type="text" name="name" value="#form.name#"></td> </tr> <tr> <td>Your Email:</td> <td><input type="text" name="email" value="#form.email#"></td> </tr> <tr> <td>Your Comments:</td> <td><textarea name="comments">#form.comments#</textarea></td> </tr> <tr> <td> </td> <td><input type="submit" name="sendcomments" value="Send Comments"></td> </tr> </table> </form> </cfoutput> <cfelse> <cfoutput> <p> Thank you for sending your comments, #form.name#. </p> </cfoutput> </cfif>
I'm not going to say anything about this code as it's a fairly typical form. This will serve as a base form that we will be adding CAPTCHA too.
There are multiple CAPTCHA solutions out there, including the built-in support in BlueDragon and Alagad's CAPTCHA component. For this demo however I'm going to use the same product I used in BlogCFC, Lyla Captcha. This is a free product and is pretty simple to get up and running quickly. Download the product and unzip it to a folder. Any folder will do. Just make sure your application can access it.
The first thing we will do in our new form is to create an instance of the CFC:
<cfif not structKeyExists(application, "captcha")>
<cfset application.captcha = createObject("component", "captchaService").init(configFile="captcha.xml") />
<cfset application.captcha.setup()>
</cfif>
Lyla Captcha is configured via an XML file. You don't need to touch it immediately though. (Although I'll be pointing to a darn good blog entry about this XML file later on.)
Now we need to add the CAPTCHA to the form. I added a new row to my table with this code:
<tr>
<td>Enter Text Shown in Picture:</td>
<td>
<input type="text" name="captcha"><br>
<!--- Captcha --->
<cfset captcha = application.captcha.createHashReference()>
<img src="captcha.cfm?hash=#captcha.hash#">
<input name="hash" type="hidden" value="#captcha.hash#" />
</td>
</tr>
There are a few things going on here. First off - I added a new text field so the user can type in the CAPTCHA text. I then ask Lyla to create a hash reference. This is a long, random string. I pass this to a CFM that will serve up an image. Lastly, I add the hash itself as a hidden form field.
Let's leave our form for a second and look at captcha.cfm:
<cfif not structKeyExists(url, "hash")>
<cfabort>
</cfif>
<cfset variables.captcha = application.captcha.createCaptchaFromHashReference("stream",url.hash) />
<cfcontent type="image/jpg" variable="#variables.captcha.stream#" reset="false" />
I do a quick check to ensure the url variable exists, and then I simply use the Lyla Captcha built in functions to get the image data. (You can also store the CAPTCHA as a physical file.)
Now let's return back to the form. To validate the CAPTCHA, I simply call one more function in the CFC:
<cfif not application.captcha.validateCaptcha(form.hash, form.captcha)>
<cfset error = error & "You did not match the image text. Try again with half a brain.<br>">
</cfif>
That's it! Lyla is pretty trivial to use and you can't beat the price. Charlie Arehart also has a blog article on how to simplify the CAPTCHA text a bit - and I definitely recommend following his suggestions:
Simplifying the captcha graphic in Lyla Captcha (and BlogCFC)
I've included all of my text files in the attachment to this blog entry. test.cfm is the original file and test2.cfm is the file with flareCAPTCHA.