A Simple CFImage CAPTCHA Plugin for ColdBox

Boston CFUG , CFML , ColdBox Add comments

Last week at the Boston CFUG meeting, we had an "open mic" night where CFUG members went up and gave short presentations in front of the group. I decided to follow up the talk that Luis Majano gave on the ColdBox framework back in July and give an intro on ColdBox plugins. I had some technical problems with my Winblows laptop early on, but Brian Rinaldi graciously let me borrow his laptop for my presentation. It was fun to present and it went well overall. But due to time constraints, I didn't get a chance to go over the code of the custom plugin that I demoed, so I decided to do it here on my blog. (Besides, it's been getting stale around here).

I built a plugin to display CAPTCHA images and validate form input against them using CF8's cfimage tag. It was pretty simple to do. To prepare, I looked at the code for another visual ColdBox plugin, the Messagebox plugin, the ColdBox docs, and this blog post by Ray Camden on CAPTCHA with cfimage. The plugin can be downloaded by clicking the link at the bottom of this post. Let's take a look at the code to see how it works.

This is the general format for the cfcomponent tag and the init() method of ColdBox plugins. The only think noteworthy about the cfcomponent tag is that I'm telling the framework to cache this plugin by adding the metadata attribute cache="true". By default, ColdBox plugins are not cached. But since this plugin us generally used over multiple requests (for displaying and then validating the CAPTCHA), I decided to have it cached. By default it uses the caching parameters in the framework's config files, but these can be overriden if desired by using the cachetimeout and cacheLastAccessTimeout attributes (see the docs for more info). Also, all plugins must extend the base plugin class (coldbox.system.plugin).

The init() function just takes the coldbox controller as an argument and sets some basic info about the plugin (name, version, description). The beauty of plugins, though, is that you don't need to worry about the init() method when you call them because the framework runs the initualization for you.

Let's take a look at the rest of the code.

The ony public methods of this plugin are display() and validate(). The display() method is essentially a wrapper for the cfimage action="captcha" tag. It's set up with default arguments so that calling "display()" will give you a 50 X 200 pixel captcha image of 3 to 5 random characters (the length will range between +/- 1 of the value in the "length" argument). Of course, you can customize the image by passing in arguments. The default random characters are built using the makeRandomString() function which was taken from Ray's blog post. But if you have your own way of generating the captcha text, you can pass that text in as an argument (in which case the "length" argument will be ignored). The "message" argument sets an error message which will appear if under the CAPTCHA image if the user is redirected back to the form after a failed CAPTCHA validation. This message can be be styled using the "cb_captchamessage" css class to make it stand out

Before the display() method sets up the image, it calls the setCaptchaCode() function. What this does is set the CAPTCHA text and a "validated" flag in a structure inside the session scope. That structure (cb_captcha) is accessed through the getCaptchaStorage() function. OOP purists out there will point out that it's not good practice to directly reference the session scope in your CFCs because it breaks encapsulation. Yes, technically, one should use a session facade, but I decided to side on pragmatism here and keep things simple. However, since I did isolate access to the session scope to the getCaptchaStorage() method, I made it very easy for anyone to modify the code to use a facade. In fact, the ColdBox "SessionStorage" plugin can be used for that purpose (since it's possible for plugins to call other plugins). Also note that the CAPTCHA text is converted to lower case before it is hashed and stored. I did this to make the validation case-insensitive. You can simply take the lcase() out to make it case-sensitive or even set up another argument to control case sensitivity.

The validate() method simply takes in a string and validates it against the CAPTCHA code that was set up to display. It then sets the "validated" flag to true or false based on if the values were equal. This can be used to redirect users back to the form or go ahead and process the form.

Usage

So here is an example of how you would use this plugin. First, you would use the display() method in your form view along with an input field for people to enter the code:

Now, in your event handler that would handle this form input, you would do something like this:

That's it! With cfimage CAPTCHA functionality in this tidy little package, you can use CAPTCHA images throughout all of your ColdBox applications. Feel free to download the plugin using the link below, use it, take it apart, and/or modify it to your heart's content.

Also, be sure to check out the two projects that were also presented at our open mic, night: Tom Mollerus's Clickheat for ColdFusion and Charles Kaufmann's Coreforms custom tag library.

Download Coldbox CAPTCHA plugin.

16 responses to “A Simple CFImage CAPTCHA Plugin for ColdBox”

  1. Luis Majano Says:
    Great stuff man!! Added to code depot
  2. Will Belden Says:
    Might be cool to have an init() or something on it, where you can specify direct-to-session or use-session-cb-plugin.

    - WB
  3. Tony Garcia Says:
    @Will
    Well, there is an init() method already. But the init() method is called by the plugin service when you call getMyPlugin(), so you wouldn't be able to directly pass any arguments to it to specify whether to use the sessionstorage plugin or not.
    Since the coldbox controller is passed into the init method of all plugins, you have access to the settings in the coldbox config file. So you could set up a setting to control for this and then the init() method could set up the captcha storage struct directly in the session scope or using the sessionstorage plugin, depending on the setting.
    If I decided to use the sessionstorage plugin, though, I'd just hardcode it into the getCaptchaStorage() method:

    <cffunction name="getCaptchaStorage"
    access="private"
    returntype="any"
    output="false">
    <cfset var sessionstorage = getPlugin("sessionstorage") />
    <cfset var cb_captcha = {captchaCode = "", validated = true} />
    <cfif not sessionstorage.exists("cb_captcha")/ >
    <cfset sessionstorage.setVar("cb_captcha",cb_captcha) />
    </cfif>
    <cfreturn sessionstorage.getVar("cb_captcha") />

    </cffunction>
  4. Glyn Jackson Says:
    thanks for the hard work here, works well. :)
  5. Tony Garcia Says:
    My pleasure, Glyn!
  6. Luis Majano Says:
    Thanks for this plugin Tony, incredibly useful and soooooo easy to integrate. I just added it to Codex Wiki and it took me about 3 minutes!!
  7. Tristan Lee Says:
    Tony, thanks for the plugin! One problem I am having though is that I'm getting an application error saying the fonts are not supported. I've checked in my CFIDE to see the fonts available and I pass is the ones that show are supported, but I still get the message. It's nothing with your plugin as this is being thrown from the cfimage tag. I ended up having to remove the font argument from the cfimage tag to get this to work. I'm running Linux so I figure that's the problem.

    Anyway, great plugin!
  8. Tony Garcia Says:
    @Tristan,
    I've heard what you described from a few people and it's happened to me as well on Linux. Basically I've found that removing the font argument as you've done is the best way of dealing with it.
    Thanks for the feedback and I'm glad you find it useful!
  9. George Murphy Says:
    Hi Tony, thanks so much for this plugin. I am using ColdBox 3.00 M4 and ColdFusion 8.0 WindowsXP SP3. I am having an issue. No matter what I do I always get a validation of false returned for the captcha. I went into the validate function in the captcha cfc and pasted this
    <cfdump var="#hash(lcase(arguments.code),'SHA')#">
    <br>
    <cfdump var="#getCaptchaCode()#">
    to test the if statement
    <cfif hash(lcase(arguments.code),'SHA') eq getCaptchaCode()>
    and the getCaptchaCode() method call is always equal to and empty string. Any idea on what might be causing this?
    Any idea on what might be going on?
  10. Tony Garcia Says:
    Hi George,
    Does the plugin work for you using ColdBox 2.6.4? I haven't tested this plugin on the 3.0 milestones yet and I know that there have been some major changes introduced in the framework. Off the top of my head, I'm not sure what can be causing your issue, but when I get a chance I'll test it with ColdBox 3 and let you know what I find.
  11. George Murphy Says:
    Hi Tony, here is what I have been able to see. In the display method of the Captcha plugin the randomly generated string is getting set correctly, but when the handler gets called the hashed generated string is no longer available for the if statement in the validate method.
    <cfif hash(lcase(arguments.code),'SHA') eq getCaptchaCode()>
    getCaptchaCode() is always an empty string.
    I tested it in 2.6 and it works there.
  12. Tristan Lee Says:
    George,

    I will post back later this evening with my code I used. I am using this on CB3, but I do recall having to modify the plugin. I just can't remember exactly what I changed and why.
  13. George Murphy Says:
    Hi Tristen, thanks for the heads up on this. I will be looking for your code. Thanks!
  14. Luis Majano Says:
    Have you used the one I modified in forgebox: http://coldbox.org/forgebox/view/Captcha
  15. Tristan Lee Says:
    George,

    Sorry for the delay. I didn't compare this to the original CFC since I don't have it on hand so you may want to do a difference test to see what I changed because it was so long ago I don't remember. I do know this works for me on ColdBox though. The only problem I have is that I can't use this along with the URL rewrite using .htaccess. For whatever reason the image will continue to try and load and load indefinitely. I don't know if that's related to the plugin itself or the way my server is handling URL rewriting.

    Anyway, hopefully this helps and works for you.
    http://megasquirt.cfcoding.com/plugins/Captcha.txt

    http://www.cfcoding.com
  16. George Murphy Says:
    Hi Tristen, thanks for the code. I tried downloading the updated code from here http://coldbox.org/forgebox/view/Captcha
    before you sent yours and with some modifications I have been able to get it working. I created an interceptor to determine what language is calling it so that it can display the proper error message. Thanks for answering.

Leave a Reply

Leave this field empty:

Powered by Mango Blog. Design and Icons by N.Design Studio