Quick and dirty CAPTCHA Guide
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.
Comments
You can store the hash reference in the session scope if you wish. It does single thread the captcha -- could be a problem if a user has a page open with the captcha and has another tab open and surfs to a page with the cpatcha.
The hash reference is the captcha text + salt (random text that auto-generated) that is hashed. It would be very difficult to break.
Best,
.Peter J. Farrell
LylaCaptcha
I agree that a hash is almost impossible to break, but that's not the danger that I'm considering. I'm supposing that IF a CAPTCHA system doesn't have an expiration for the hash (or if it has an unreasonably long expiration for the hash, such as 30 minutes), that a malicious human user might be able to hand the hash value and the answer text value off to a bot. The bot could then abuse the site until the hash expired. Is that correct, or am I missing something else?
My scenario may not be very likely, but it could be used to spam every blog entry of someone's site in a few minutes. Also, I'm aware that LylaCaptcha does default to expiration of hashes, so please don't consider this a criticism of your software or of anyone else's-- it's more of an academic discussion of what might be possible under a poor CAPTCHA implementation by a site developer.
Aaron, Lyla will run on Linux. I have it running with CF on CentOS 3 (which is the Open Source version of RHEL3). You must be missing one of the optional packages for RH like x11-deprecated-libs which has the fonts etc. Does your cfcharting work? Also, are you running the RH machine as headless? I've gotten reports on Lyla running on headless machine, but it has to be properly configured.
LylaCaptcha does work on RedHat Linux environment without issue. I am sure that it is permission issue on RedHat Linux. Your name is familiar to me and I think your site is at web hosting provider where I am working. Drop email at support department with subject "Atten Shaji: Captcha issue". I am happy to look on it for you.
Uncompressed folder: 76799 bytes contained:
captcha.xml 7k
captchaService.cfc 38k
captchaServiceConfigBean.cfc 22k
lylaCaptchaLicense.txt 11k
It is actually a question and not a comment but I only see a broken image in test2.cfm. I am using ColdFusion MX 6.1. Any help is appreciated.
Thank you
Attribute validation error for tag CFCONTENT.
The tag does not allow the attribute(s) VARIABLE. The valid attribute(s) are DELETEFILE,FILE,RESET,TYPE.
The error occurred in D:\Inetpub\wwwroot\library\farrah\lyla\captcha.cfm: line 6
4 :
5 : <cfset variables.captcha = application.captcha.createCaptchaFromHashReference("file",url.hash) />
6 : <cfcontent type="image/jpg" variable="#variables.captcha.fileLocation#" reset="false" />
I recently made updates to this examples based off the simplification steps found here: http://carehart.org/blog/client/index.cfm/2006/8/1...
However, after making the updates and refreshing I'm not seeing the updates on test2.cfm. Is there a reset tag somewhere, are the settings cached? How can I refresh the settings?
Thanks!
Brett
Really a very nice post.
Can you please specify that LylaCaptcha works with ColdFusion 6.1 or not? I am facing many issues with that.
I am very new to ColdFusion. It would be great if a step by step guide can be provided for beginners like me.
Any help would be highly appreciated.
Would certainly wait for your replies.
Thanks and Regards,
Haseeb Khan
http://www.naperville-lib.info/captcha.cfm?hash=10...
Element INSTANCE.HASHREFERENCECACHETIMESTAMP is undefined in VARIABLES.
The error occurred in C:\Websites\132648ha2\captchaService.cfc: line 937
Called from C:\Websites\132648ha2\captchaService.cfc: line 680
Called from C:\Websites\132648ha2\captchaService.cfc: line 659
Called from C:\Websites\132648ha2\captchaService.cfc: line 120
Called from C:\Websites\132648ha2\captcha.cfm: line 5
935 : </cffunction>
936 : <cffunction name="getHashReferenceCacheTimestamp" access="public" returntype="numeric" output="false">
937 : <cfreturn variables.instance.hashReferenceCacheTimestamp />
938 : </cffunction>
939 :
***************
Thanks for the open source code. Hope this work for me as I am tired of receiving spam.
<config name="hashValidPeriod" value="1800000"/><!-- in milliseconds -->
Where is the hash value coming from for this page?
http://www.naperville-lib.info/captcha.cfm?hash=BE...
Finally it is working fine on my PC (which acts as test server), after I configured the testing for the folder with the test server. However when I upload to our website the image doesn't get displayed(hosted by hostmysite.com). I am quite new to cf. What do I need to configure on the website server for this to work? Hey it looks like I am getting closer to get this to work, keeping my finger crossed.
Thanks
http://www.naperville-lib.info/captcha/test2.cfm
There is no option to view the image, however I can go to properties and get the error link, which is;
http://www.naperville-lib.info/captcha/captcha.cfm...
I just will let you know that after emailing Peter the problem, figured out that I had to download the newer version of Lyla, then it started to display the image.
http://www.flexroofingsystems.com/captcha/test2.cf...
I checked with the hosting service and they cleared the cache and still no changes.
Any other ideas?
Thanks!
<cfif not structKeyExists(application, "captcha")>
<cfset application.captcha = createObject("component", "captchaService").init(configFile="captcha.xml") />
<cfset application.captcha.setup()>
</cfif>
This caches your settings. Add this to the CFIF
or isDefined("url.init")
Then hit your site with ?init=1 in the URL.
So sorry, however, I am hopelessly not a programmer and all I can do is cut and paste what sombody shows me. Would you be so kind as to put that code in the CFIF for me because I don't know where it goes? I can then put it in place and do the new url as you indicated.
Thanks so much!
<cfif not structKeyExists(application, "captcha") or isDefined("url.init")>
<cfset application.captcha = createObject("component", "captchaService").init(configFile="captcha.xml") />
<cfset application.captcha.setup()>
</cfif>
Good Luck!
Have you had any problems with this rendering in Firefox and not IE? I am getting the big X like there is no image in IE. Any thoughts?
Thank You!
PS: Be wary of the code contained in the PDF in LylaCaptcha_v1Beta.zip - some variables were not fully qualified - the correct function call begins with the word 'create' - and the order of arguments for one of the functions was reversed. Other than that, Awesome! (And thank you Lyla!)
You're a star!
Just in time compilation error
Invalid parser construct found on line 4 at position 74. ColdFusion was looking at the following text:
.
Invalid expression format.
Also on test.cfm i got this:
An error occurred while evaluating the expression:
structKeyExists(variables, "error")
Error near line 28.
I have very little exposure in CF and would greatly appreciate your help. Thanks.
do you know of any other solutions out there which would work with cf5?
it is important to restart coldfusion - ask your hosts to do it which they will do at their convenience.
(On your local machine the easiest way it to just restart your pc.)
Thanks for the files.
We use it on this site www.medarc-limited.co.uk
So.. I'm trying the demo (the one with test.cfm and test2.cfm), but when running test2.cfm I keep getting an error in line 3 "Variable application is undefined."
I've unzipped all the files in one directory.
The error is with MX7 running on a Linux server.
Anyone here who might know what could be wrong?
Thanks so much
Rick
<!--- create captcha --->
<cfif not structKeyExists(application, "captcha")>
<cfset application.captcha = createObject("component", "captchaService").init(configFile="captcha.xml") />
<cfset application.captcha.setup()>
</cfif>
You can modify the CFIF to look for url.reinit so you can force a refresh.
<cfif not structKeyExists(application,"captcha") or isDefined("url.reinit")>
or
<cfif not structKeyExists(application,"captcha") or structKeyExists(url,"reinit")>
I encountered some problems installing Lyla for the first time. On my local machine running CF8, everything is fine.
On my production server running CF MX 6.1, none of my images appear.
Looking at captcha.cfm, I get the following error:
=====================
Attribute validation error for tag CFCONTENT.
The tag does not allow the attribute(s) VARIABLE. The valid attribute(s) are DELETEFILE,FILE,RESET,TYPE.
4 :
5 : <cfset variables.captcha = application.captcha.createCaptchaFromHashReference("stream",url.hash) />
6 : <cfcontent type="image/jpeg" variable="#variables.captcha.stream#" reset="false" />
=====================
Any known workarounds for those of us on CF MX 6.1?
Note I did change the cfcontent type to "image/jpeg" from "image/jpg" which I read was also required - can anyone confirm this?
Thanks for your great support,
Chris
Unfortunately, it did not completely solve the problem.
Now I'm receiving the following error:
====================
Error Occurred While Processing Request
Element INSTANCE.HASHREFERENCECACHETIMESTAMP is undefined in VARIABLES.
The error occurred in \Htdocs\test\lyla\captchaService.cfc: line 936
Called from \Htdocs\test\lyla\captchaService.cfc: line 680
Called from \Htdocs\test\lyla\captchaService.cfc: line 659
Called from \Htdocs\test\lyla\captchaService.cfc: line 120
Called from \Htdocs\test\lyla\captcha.cfm: line 5
====================
This is getting much deeper than my level of understanding so I may need to find an alternative solution.
If anyone has additional input for us MX 6.1 users, it would be appreciated.
Thanks for your expertise -
Chris
====================
"You cannot server via a stream on 6.1 - you'll need to use the file type createCaptchaFromHashReference("file", url.hash") instead and then use a .cfm page that serves that file via cfcontent."
====================


In the non-quick-and-dirty implementation of a CAPTCHA, do you think there would be any advantage to storing the hash value in SESSION scope? That way you wouldn't have to expose the hash value to the user in the HTML code.
I know that if there's an expiration period to the hash that it's not a big risk in letting the user see it. But I'm curious what you think.