A while back, I wrote about how easy ColdFusion 8 is going to make image creation and manipulation. Of my multipart series on ColdFusion 8's new CFImage tag, I briefly touched upon its CAPTCHA functionality. A few people have contacted me since looking for a more detailed example of how CAPTCHA can actually be used. Before I show you the code, let's just quickly cover the basics. With the CAPTCHA action of the CFImage tag, there are few attributes that are required:
Height - The pixel height of the generated image. The height must be large enough to properly display all of the designated text at the given font size.
Width - The pixel width of the generated image. The width must be large enough to properly display all of the designated text at the given font size. This width is directly proportional to the number of characters, so once you find a width that works, just stick with it. If the width is too small, ColdFusion will throw an error (which will actually tell you what the minimum width can be for the given text).
Text - The text to display in the CAPTCHA image. The CAPTCHA action does not randomly select text for you; it only displays the text that you tell it to. It is still up to us as programmers to generate random text and tell it what to display.
As you adjust the height and width of the tag, ColdFusion 8 automatically spreads out the individual characters of the designated text to fill up the area. The text may or may not be evenly distributed; ColdFusion tries to move things around enough to make it bot-proof, but human readable.
Additionally, there are some optional ColdFusion 8 CAPTCHA attributes:
Destination - By default, ColdFusion 8 writes the CAPTCHA image to a randomly named image in the image servlet and then writes that image to the browser (recommended IMO). However, if you provide a destination (an absolute or relative path) file, ColdFusion 8 will write the CAPTCHA image to this file rather than writing it to the browser. If you do this, you have to be careful not to overwrite a CAPTCHA image that a concurrent user might be viewing.
Difficulty - The degree to which the text is made bot-proof. Bey default, the difficulty is Low (options include Low, Medium, High). The higher the difficulty, the less likely it is that both Bots and Humans can read it. The trade-off is your choice, but I feel that Medium is a nice level of security.
Overwrite - When using the Destination attribute, this is a flag to determine whether or not to overwrite the existing file at the given destination path. This defaults to No, and if you attempt to overwrite an image without this being set to Yes, ColdFusion 8 will throw an error.
Fonts - This is a comma separated list of fonts that can be used to generate the CAPTCHA image. If you give is a list of several fonts, it will randomly pick a font for EACH character of the CAPTCHA text. Only certain fonts can be used (of which the rules are not entirely clear to me).
FontSize - The point (pt) size of the font to be used in the generated image. FontSize will affect the minimum width of the result image. It will also affect readability, so be sure to chose a font size that will be readable by your users.
Ok, so now that we have a basic understanding of the CFImage / CAPTCHA tag, let's take a look at the example:
<!--- Kill extra output. ---> <cfsilent> <!--- Param FORM values. ---> <cfparam name="FORM.captcha" type="string" default="" /> <cfparam name="FORM.captcha_check" type="string" default="" /> <cftry> <cfparam name="FORM.submitted" type="numeric" default="0" /> <cfcatch> <cfset FORM.submitted = 0 /> </cfcatch> </cftry> <!--- Set a flag to see if this user is a bot or not. ---> <cfset blnIsBot = true /> <!--- Check to see if the form has been submitted. ---> <cfif FORM.submitted> <!--- Decrypt the captcha check value. Since this was submitted via a FORM, we have to be careful about attempts to hack it. Always put a Decrypt() call inside of a CFTry / CFCatch block. ---> <cftry> <!--- Decrypt the check value. ---> <cfset strCaptcha = Decrypt( FORM.captcha_check, "bots-aint-sexy", "CFMX_COMPAT", "HEX" ) /> <!--- Check to see if the user-submitted value is the same as the decrypted CAPTCHA value. Remember, ColdFusion is case INsensitive with the EQ opreator. ---> <cfif (strCaptcha EQ FORM.captcha)> <!--- The user entered the correct text. Set the flag for future use. ---> <cfset blnIsBot = false /> </cfif> <!--- Catch any errors. ---> <cfcatch> <!--- Make sure the bot flag is set. ---> <cfset blnIsBot = true /> </cfcatch> </cftry> </cfif> <!--- Before we render the form, we have to figure out which CAPTCHA text we are going to display. For this, we have to come up with a random combination of letters/numbers. For this, we are going to use an easy solution which is shuffling an array of valid characters. ---> <!--- Create the array of valid characters. Leave out the numbers 0 (zero) and 1 (one) as they can be easily confused with the characters o and l (respectively). ---> <cfset arrValidChars = ListToArray( "A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z," & "2,3,4,5,6,7,8,9" ) /> <!--- Now, shuffle the array. ---> <cfset CreateObject( "java", "java.util.Collections" ).Shuffle( arrValidChars ) /> <!--- Now that we have a shuffled array, let's grab the first 8 characters as our CAPTCHA text string. ---> <cfset strCaptcha = ( arrValidChars[ 1 ] & arrValidChars[ 2 ] & arrValidChars[ 3 ] & arrValidChars[ 4 ] & arrValidChars[ 5 ] & arrValidChars[ 6 ] & arrValidChars[ 7 ] & arrValidChars[ 8 ] ) /> <!--- At this point, we have picked out the CAPTCHA string that we want the users to ender. However, we don't want to pass that text anywhere in the form otherwise a spider might be able to scrape it. Thefefore, we now want to encrypt this value into our check field. ---> <cfset FORM.captcha_check = Encrypt( strCaptcha, "bots-aint-sexy", "CFMX_COMPAT", "HEX" ) /> </cfsilent> <cfoutput> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html> <head> <title>ColdFusion 8 CFImage / CAPTCHA Demo</title> </head> <body> <h1> ColdFusion 8 CFImage / CAPTCHA Demo </h1> <form action="#CGI.script_name#" method="post"> <!--- This is the hidden field that will flag form submission for data validation. ---> <input type="hidden" name="submitted" value="1" /> <!--- This is the hidden field that we will check the user's CAPTCHA text against. This is an encrypted field so that spiders / bots cannot use it to their advantage. ---> <input type="hidden" name="captcha_check" value="#FORM.captcha_check#" /> <p> <!--- Output the CAPTCHA image to the browser. Here, we are using a difficulty of medium so that we don't fry the user's brain. ---> <cfimage action="captcha" height="75" width="363" text="#strCaptcha#" difficulty="medium" fonts="verdana,arial,times new roman,courier" fontsize="28" /> </p> <label for="captcha"> Please enter text in image: </label> <input type="text" name="captcha" id="captcha" value="" /> <input type="submit" value="Submit" /> <br /> <!--- Check to see if the form has been submitted so we can see if we need to show the validation. ---> <cfif FORM.submitted> <h3> Bot Validation Results </h3> <!--- Check for a bot. ---> <cfif blnIsBot> <p> You Are A Bot!!! </p> <cfelse> <p> You are not a bot :) </p> </cfif> </cfif> </form> </body> </html> </cfoutput>
In my example, I am using a medium difficulty CAPTCHA image. This seems to work well. Also, I have left a lot of code in the example so that you can see fully how you might go about selecting a random CAPTCHA value and then checking against it. A good deal of this code would be factored out and placed in some sort of function library. But, for the purposes of education, I don't like to have "black-boxed" functionality.
When using CAPTCHA, it is not enough to just provide the CAPTCHA image and then an input for the user. We also need to check the user-entered value against what the original CAPTCHA text was. Therefore, we need to pass along the original text with the form submission. However, we don't want this text to be scrappable by the Bots. Therefore, I am encrypting the original text and submitting that via a hidden form field. Once the form is submitted, I then decrypt it and check that it matches the user entered value.
Running the above page, we get a screen that looks like this:
As you can see, ColdFusion 8 is going to make creating CAPTCHA images really easy. In fact, the bulk of the code here goes into the HTML and form validation; actually creating the CAPTCHA image is just ONE LINE of code. Snazzy!
Want to use code from this post? Check out the license.