Ruby on Rails
HowtoSecureFormsWithNoisyImages

This is broken. Running the resulting image through gocr (http://jocr.sourceforge.net/) gives you the challenge text with a high degree of accuracy. This is not a problem for bots. Reducing the offset between characters to 15 makes it harder because it makes it a problem of segmentation rather than character recognition.


How do you secure forms from script kiddies? Here’s a quick and dirty way of creating a noisy code image to protect your forms.

First a brief discussion of the technique. Preceding the form you show a page with a randomly generated noisy code image which the user is asked to type into an input field. The value submitted is then compared to the code stored in the :session[] and the form is shown only if they match. If not it redirects to the previous page. The image is made noisy to deter a recognition routine from automatically figuring out the code which would defeat the whole thing.

First when faced with this requirement for a php based application, I had used the php’s gd library to create the noisy code image. I was wondering how to do this in rails until I came upon the really neat ruby interface to imagemagick – RMagick library. I was able to generate the image with imagemagick and then send it to the browser using rails own send_data routine.

First I created a ruby class called Noisymage in a noisy_image.rb file stored in the models directory.

Model part:

class NoisyImage

require 'RMagick'
include Magick

attr_reader :code, :code_image
  Jiggle = 15
  Wobble = 15

def initialize(len)
  chars = ('a'..'z').to_a - ['a','e','i','o','u']
  code_array=[]
  1.upto(len) {code_array << chars[rand(chars.length)]}
  granite = Magick::ImageList.new('granite:')
  canvas = Magick::ImageList.new
  canvas.new_image(32*len, 50, Magick::TextureFill.new(granite))
  text = Magick::Draw.new
  text.font_family = 'times'
  text.pointsize = 40
  cur = 10

  code_array.each{|c|
    rand(10) > 5 ? rot=rand(Wobble):rot= -rand(Wobble)
    rand(10) > 5 ? weight = NormalWeight : weight = BoldWeight
    text.annotate(canvas,0,0,cur,30+rand(Jiggle),c){
      self.rotation=rot
      self.font_weight = weight
      self.fill = 'darkred'
    }
    cur += 30
  }
  @code = code_array.to_s
  @code_image = canvas.to_blob{
    self.format="JPG" 
  }
end

end

In the code above a random array of (len) is created using ruby’s rand function. Notice that the vowels were stripped out so as to minimize the accidental creation of unsavory words. I also add a bit of Jiggle (up and down) and Wobble (rotational) for each character in the array using random values again. The resulting image is converted to an in memory JPG and stored in the instance variable @code_image and so is the ascii code in @code.

Controller part:

def protect
    unless session[:noisy_image]
        session[:noisy_image] = NoisyImage.new(6)
    session[:code] = session[:noisy_image].code
    end
end

def code_image
    image = session[:noisy_image].code_image
    send_data image, :type => 'image/jpeg', :disposition => 'inline'
end

def check_code
    typed_code = params[:typed_code]
    unless typed_code == session[:noisy_image].code
      flash[:notice] = "Please type in the correct code" 
      redirect_to(:action => "protect") and return false
    end
    redirect_to(:controller => "FormController", :action => "show_form")
end

Next add three actions like the ones above to a form protection controller called say, Form_protect. The action protect instantiates NoisyImage class and stores it in the :session. It is called to display the Form_protect.rhtml. The code_image action is called from the form_protect.rhtml page as a src for an image tag.

View Part:

<img src="/Form_protect/code_image">
<%= form_tag { :action => :check_code } %>
<p>Enter the exact 'Code' from Image above: </p>
<%= text_field (:typed_code)%>


Obviously all of this could be wrapped into just one controller. The RMagick library has a wealth of capabilities to generate just about any kind of an image. So code away and enjoy! -bakki


Just for the sake of people looking for this type of code, noisy image == captcha. Now when people look for captchas for Rails, they can find this page. -lori


If someone opens many pages with these images at once (say, to queue up many blog entries in tabs, each with comment forms), won’t this code fail all of their attempts to comment unless they reload each page before doing so? – Tom


Don’t forget that if you only provide a visual captcha, you’re leaving out users that use a screen reader or vision problems. If you take this approach consider providing both a graphic image and an audio file alternative.


Note, that noisy images are not a problem for advanced (non-custom) OCR software. Thus a script using wget and some commandline style OCR software that is capable of handling such images would break the security easily.
To cut a long sentence short: noise does not secure your image. It just makes it harder to read for users. A better choice for securing your forms using CAPTCHAs would be displacement filtering (twirl, polar coordinates, etc.). This way most of the patterns that ORC could use is removed.

Either remove the noise and go with text in images, simple to break but may stop some scriptkiddie. Or (the better choice), implement good captcha, reading through the article at wikipedia (and follow some of its links, read the research on this topic) should be a good start.


RMagick config is less than ideal under OSX. I encountered two problems, and Google shows these are common issues.
(1) “sh: line 1: gs: command not found”. This was due to RMagick not finding gs (I think both were installed with OpenDarwin). The quick solution was a symlink to set up /usr/bin/gs (“ln -s /opt/local/bin/gs /usr/bin”). You could probably change the path in .cshrc or somewhere, or in the configuration used to build RMagick, I’m not sure. Anyway, the symlink worked fine.
(2) ””unable to read font `times`”. This is caused by a font config issue and fixing it looks to be a major hassle. Again, there’s fortunately a way to bypass the whole issue with a quick fix. It turns out that you can set the entire font to point to a direct ttf file. So for this captcha solution, simply delete the line above involving text.font_family and replace it with: text.font = ’/path/to/font.ttf’
You’ll need to locate an appropriate ttf file (do a “locate ttf” to find the ttf files on your machine), and you’ll probably want to copy it to your Rails app, so you can make the path relative.
—Mahemoff
compile error
../config/../app/views/form_protect/_form_protect.rhtml:2: parse error, unexpected tASSOC, expecting ‘}’
_erbout.concat(( form_tag {action => :check_code} ).to_s); _erbout.concat ”\n”


I was able to overcome the ‘times’ font issue another way (not that it’s better, just different).

In the NoisyImage class that the author of this solution so generously provided, I made the following change on or around line 18:

I commented out text.font_family = ‘times’ and replaced it with text.font_family = ‘arial’

Which is to say that I changed the font from times to arial. I am not sure what other fonts work, but this one seems to.

Also, what seems to be missing from the controller example is the view action.

At it’s most basic, it should look like this:

def view protect
end

I am running on a windows xp box using Radrails & InstantRails. I hope this helps someone in their efforts.

-Noah