Monday, June 18, 2007

RA-captcha - An idea for ajax based captcha for rails applications.

While working on a new feature for paahijen-Scratchpad, we needed a captcha mechanism to keep the spam-bots out of the door! Since some of our users may not be quite comfortable with typing convoluted roman alphabets, we needed a different system. Honestly, I was too lazy to try out all the Rimage giri to get something useful and somehow wanted a mechanism, which is lightweight, easy to implement (lesser code, no new learning.. ;-) ). Somewhere, I had read about "arithmetic" captcha, and we decided to use it. We just hit upon an interesting idea... to use Ajax for captcha.. Hence the name RA-captcha (rails ajax captcha)..

The idea is fairly simple.. What it basically does is uses link_to_remote to update the contents of a "div" in the form dynamically with a text for a simple arithmetic equation. The equations is so simple that a 5th grade average student can do them without troubles.. eg. the equation will be something like "8 + 2 = " and then an input text box is provided to enter the value.. Though this technique uses "link_to_remote", it is not too hard to use other ajax methods for the rails as the need be!

Here is a simple code that explains the idea... The code is not perfect and I am not a rails expert, Also it doesn't do any validations than minimum.

Create an application "ajax-test" using rails ajax-test

We are going to add a controller called "main" controller and corresponding views. For this example we don't need any ActiveRecord models.

script/generate controller main



We are going to create the view for this controller and action "index". No layouts..
So create a directory called main inside app/controllers and edit the file
app/views/main/index.rhtml with following contents.




<%= javascript_include_tag "prototype", "effects" %>


<h4>
<%= link_to_remote ( "Click to open form!" , { :before => "Element.show('test-form');",
:url => { :action => "getequ" },
:update => "equdiv" }) %>

</h4>

<div id="test-form">
<%= start_form_tag({:action => "submit_test"}, {:id => "t_form"}) %>

<%= text_area_tag "userinput", nil, :size => "20x10" %>

<p><b>Humans? Please do some simple Math!
<div id="equdiv"> </div><input type="text" name="capval" size="2" />
</b>
</p>
<%= submit_tag "Go!" %>
<%= end_form_tag %>
</div>

<script>
Element.hide("test-form");
</script>



The controller code looks like this.....


class MainController < ApplicationController

def index
end

def getequ
numbers = (0..9).to_a

@equ = ''
num1 = numbers[rand(numbers.length - 1)]
num2 = numbers[rand(numbers.length - 1)]

session[:capsum] = num1 + num2

@equ << num1.to_s + " + " + num2.to_s + " = "
render(:inline => @equ, :layout => false)
end

def submit_test
if request.post?
if params[:capval].to_i == session[:capsum]
@userdata = params[:userinput]
flash[:notice] = "your input is accepted!!"
else
flash[:notice] = "Sorry you got the math wrong! can't accept your input!"
end
end
# reset the sum so one cannot simply press 'back' buttton and re-use it!!
session[:capsum] = -100

end

end


There is an additional view defined for submit_post. Which looks like this...


<% if flash[:notice] %>
<%= "<><b> #{flash[:notice]} </b> </p>" %>
<% end %>
<% if @userdata %>
<%= "You just entered -- #{@userdata}" %>
<p> <%= link_to "again", :action => "index" %>



<% else %>
No input data click <%= link_to "try again", :action => "index" %> to start.
<% end %>



How this works is fairly simple. Basically, whenever the first link is clicked an action in the controller called getequ is called, which generates the equation string for us and stores the value for the equation in the session[:capsum] variable. When the form is subimtted, the value of the text field is checked agains the value stored in the session[:capsum] variable. If the values match, the input is allowed, if they don't match the input is not allowed and the view for submit_test is rendered accordingly..

Since, the value the user has to input is only available upon loading the page and updating div, this value simply will never be known to spam-bot to be used without actually loading the page..

---------

The idea looked so simple to me and pilot to be overlooked for so loong. May be there is a catch which I am not getting yet!! Hopefully someone will point out....


PS: Editing html on blogger is a big pain!! eg. it gets rendered. One way out is to change the "<" of each html tag with "&lt;" and then blogger doesn't try to render it.....

5 comments:

Mukul Kumar said...

Hi Abhijit,

Very interesting idea.

How is the “session[:capsum]” variable stored?

I can tell you one thing, that I have done recently – is to scrape the dynamic parts of a DHTML web pages (using PHP and curl). Basically we read the parts of a web page (div’s) that are dynamically populated using Javascript or XHR (using innerHTML). So, it is possible to read dynamically populated DOM variables, I am sure session variables too. So, in that case it may be possible to scrape the left-hand-side of the equation, therefore calculate this equation and therefore break the captcha.

Even with this will still be a captcha, however a weak captcha. I have seen several captcha implementations, some are very strong, to an extent that a lazy human (like me) would find it objectionable to read the text and type it in the form. I have seen weak captcha, where they just have a handmade textual pattern that you need to guess.

My suggestion will be, unless you are a target of a major DOS attack, you shouldn’t spend much time on a captcha, just fabricate a few dozen images and use those. Or if the above implementation is easy enough, just use that.

I hope that helps.

Thanks,
Mukul.
http://mukulblog.blogspot.com

Siddharth said...

Hi Mukul,

Session variables are stored on server, in memory or in the db. I don't think one can find out the LHS of the captcha equation Abhijit is talking about.

So, If the bot is intelligent enough, it can read the dynamically populated equation but not the answer.


Thanks,
Siddharth

Abhishek said...

Hey this idea has been implemented. It's good - but once spambots find out about this, I dont think it should be a challenge to screenscrape equations and then spam. check out the blogs.sun.com/abhishekn

Sun blogs are using this, and AFAIK a lots of others too. Its a very viable lightweight CAPTCHA system.

Abhijit said...

Abhishek,

The idea is not about equations ie. arithmetic, the idea is about generating div content dynamically which the spambots cannot get easily unless they somehow read the contents of the div also... (which is somewhat harder without XHR).. Having said this.. it does not mean that this is not hard to read the equations at all, it is just about increasing the "cost" for the spambots!

Abhijit said...

Just out of curiousity, I was checking the implemntation of the equation for CAPTCHA on blogs.sun.com . Unfortunately, it generates the same equation evertime... I should say pretty lame???