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="email@example.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:
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