(4,764 views)

Sorting through CAPTCHA for Rails

Wanting to add a CAPTCHA for my Contact form, I was challenged by a number of technical and aesthetic issues.

I want to prevent spambots from using the Contact Us form on my Rails 3 site, so using a "captcha" widget seems like a good idea. You've seen them I'm sure, where you're required to enter random text shown in a distorted image.

INSTALLING AND USING reCAPTCHA

Looking through Ruby Toolbox for a CAPTCHA gem, the hottest one is reCaptcha, an API to the free Google reCAPTCHA service. It's very robust, ajax-based, configurable (and helps support digital books?! whatever.)

First, create a reCAPTCHA key at http://www.google.com/recaptcha/whyrecaptcha. I decided to make it a global key which will work across my domains, but you can make one that's domain-specific.

Add the gem to your Gemfile

    gem "recaptcha", :require => "recaptcha/rails"

 

And create an initializer: config/initializers/recaptcha.rb

    Recaptcha.configure do |config|
      config.public_key  = '6Lc6BAAAAAAAAChqRbQZcn_yyyyyyyyyyyyyyyyy'
      config.private_key = '6Lc6BAAAAAAAAKN3DRm6VA_xxxxxxxxxxxxxxxxx'
    end

 

Add the recaptcha to the form's view. In my case I'm using HAML to template my html, and simple_form for my form builder, so the following code works for me:

    .input{ :style => "height: 130px" }
      %label Verify
      = recaptcha_tags :display => {:theme => 'blackglass'}
      - if @contact_form.errors[:recaptcha]
        %span.error= @contact_form.errors[:recaptcha].join(' ')

 

Note I'm using the :theme option for a style that matches my site.

Finally, handle the captcha from the submitted form in your controller. For me its the "create" action in the ContactFormsController:

        if verify_recaptcha(:model => @contact_form, :attribute => :recaptcha, :message => "doesn't match, please try again") && 
          @contact_form.save
          ...         

 

And added an "attr_accessor :recaptcha" to the ContactFrom model.

OK. Fire up the app (rails server)... oops, we get an error

        Recaptcha::RecaptchaError (uninitialized constant Net::HTTP):

 

A little googling reveals this: "As a temporary hack you can add require 'net/http' just before require 'rails/all' in your Rails 3 config/application.rb". I do this and voila! Here's the result:



And it works. But boy is it ugly! and confusing to use, and really distracts visually from the purpose of the page. Time to look for an alternative.

INSTALLING AND USING simple_captcha

Simple_captcha is a simpler version of captcha, both visually and functionally. Fortunately it offers sufficient functionality for my purposes: a random distorted image of human readable text which would confuse all but the most aggressive spanbots. I do not need accessibility features of reCAPTCHA (e.g. audio support for blind users). And it's smaller and easier to style.

The original simple_captcha is a Rails plug-in which can be found here. Fortunately, others have made mirrored it on Github, made it available as a Gem, and even updated it for Rails 3.

It uses the ImageMagick toolkit to generate the Captcha images on the fly, via the rmagick Gem. I'm running on OSX, and first needed to install ImageMagick. There's various ways to do this, I decided to use MacPorts:

  $ sudo port selfupdate
  $ sudo port install ImageMagick

 

Note, this also requires Apple Developer ADC tools.

To integrate with your app, first add the gem to your Gemfile. Also need rmagick.

  gem 'simple_captcha', :git => 'git://github.com/galetahub/simple-captcha.git'
  gem 'rmagick', :require => false

 

Then run the built-in generator to migrate the simple_captcha_data database table:

  $ bundle install
  $ rails generate simple_captcha
  $ rake db:migrate

 

Next, add it to the application_controller.rb:

  ApplicationController < ActionController::Base
    include SimpleCaptcha::ControllerHelpers   

 

Add it to the form's view (I'm using HAML syntax)

  = form.simple_captcha

 

And handle the captcha from the submitted form in your controller. For me its the "create" action in the ContactFormsController:

  if simple_captcha_valid? && @contact_form.save
    ...

 

I didn't really like the default layout, so I replaced the generated partial: views/simple_captcha/_simple_captcha.erb with my own version (_simple_captcha.html.haml)

    .input.captcha
      %label.text Verify
      = @simple_captcha_options[:field]
      =raw @simple_captcha_options[:image]
      %span.hint please enter the characters you see in the image 

 

Taadaa!! It works, and looks nice.


But....... it doesnt work when deployed to Heroku :(

USING simple_captcha on HEROKU

I'm not 100% sure what's wrong with running simple_captcha on Heroku (bamboo-mri-1.9.2 stack), but the captcha image does not get generated. I spent too much time trying to trace the issue, googling for answers, and posting on mailing lists. I tried several other simple_captcha mirrors and forks on Github, to no avail. Unfortunately, I didn't document all the nuances of what did or didnt work as I fiddled around. So I decided to just go back to the original SVN-based plug-in, which in the end, did require some patches to get working with Rails 3.

First, remove the 'gem simple_captcha' from the Gemfile, and run 'bundle install'.

Then install the plug-in

  $ rails plugin install svn://rubyforge.org/var/svn/expressica/plugins/simple_captcha ; 

 

The "rake simple_captcha:setup" has errors in Rails 3, but I can use the migration and partial view generated above.

In your routes.rb, add:

    match 'simple_captcha/:action', :controller => 'simple_captcha'

 

Firing up the Rails server, we immediately get an error that "session_id is undefined". Edit the file vendor/plugins/simple_captcha/lib/simple_captcha_setup.rb, and replace session.sesson_id with request.session_options[:id] as follows:

    def simple_captcha_key #:nodoc
      session[:simple_captcha] ||= Digest::SHA1.hexdigest(Time.now.to_s + request.session_options[:id].to_s)
    end   

 

On my system, I also got a runtime segmentation fault. Googling it, StackOverflow.com comes to the rescue, I took the following steps:

    $ sudo port uninstall ImageMagick
    $ sudo port edit ImageMagick
    Add --disable-openmp to configure.args (near line 100)
    $ sudo port install ImageMagick
    $ gem uninstall rmagick
    $ gem install rmagick

 

Cool, cool, cool. It runs find in development environment. Deploy to Heroku, and it runs fine there too in Production mode.

Comments

by Andrew on Apr 20, 2011

>>

Really wish I would've found this two days ago... Still useful now though. Thanks!

by urjit on Sep 10, 2011

Sorting through reCAPTCHA for Rails

thanks man...that is so far the best explanation, specialy for beginners.

by g on Nov 15, 2012

>>

afssf

New Comment

markdown formatting permitted