Friday, June 21, 2013

Ruby on Rails, Recaptcha, AJAX

(excuse the formatting of this post, I can't be bothered right now to deal with the Blogger rich-text editor:P)

So I launched www.capoapp.ca and www.bitesite.ca over a year ago and it was only a matter of time before bot spammers started hitting my website. I quickly started seeing all these nonsense e-mail notifications from people filling out my feedback and contact forms among other public forms.

Without even knowing they were bots, my first attempt to stop these spammers was using a Captcha solution. So I decided to use the www.recaptcha.com and more specifically, the recpatcha gem found here.

I used this on www.capoapp.ca and luckily, within days, the spam stopped. It looks like I had found my solution.

So I implemented recaptcha on www.capoapp.ca's Feedback page and Submit an Open Mic page and all was good.

My next step was to implement it for my company's corporate website, www.bitesite.ca.

I added the recatpcha_tags as instructed, and also added the verify_recaptcha code to the server-side controller (just like I did with www.capoapp.ca), but to no avail.

Capo was working perfectly fine but BiteSite wasn't. What gives?

The problem was actually quite obvious (I must have just not slept well - well actually I was coding BiteSite's solution when I was 12-hours jet-lagged). And while the problem was obvious, the solution wasn't really.

What was the problem? Well it had to do with the submission of the form. In Capo, I was submitting the form by posting the page to the server - and because the recatpcha_tags were part of the form, they automatically went with the form to the controller - and all the necessary components were there for verify_recpatcha to process.

BiteSite on the other hand, I was submitting the form via a JQuery AJAX call. So the fields in my form aren't automatically submitted. In the past, here's what my BiteSite contact form submit AJAX code looked like:

var first_name = $("#first_name").val();
var last_name = $("#last_name").val();
var email_address = $("#email_address").val();
var message = $("#message").val();
$.ajax({
  url:"/contact",
  type: "POST",
  data: { first_name : first_name,
          last_name : last_name,
          email_address : email_address,
          message : message },
  dataType: "json",
  success:function(data, textStatus, jqXHR){
    ...
  },
  error:function(jqXHR, textStatus, errorThrown){
  ...
  }
});
So as you can see, I'm manually passing first_name, last_name etc. to my controller. The issue is that for verify_recaptcha, I need to pass what the user entered for the recaptcha_tags. On top of that, I need to pass any additional information that verify_recaptcha needs to do its work.
So that was the problem?

What's the solution? I need to figure out exactly what to pass to my controller so that verify_recaptcha will work. So luckily, the recatpcha gem is open-source, and I just browsed the code to find that verify_recatpcha relies on 2 main parameters:
params[:recaptcha_challenge_field]
params[:recaptcha_response_field]
So that was it. My Ajax call was missing these two parameters, so all I had to do was change my code to this:
var first_name = $("#first_name").val();
var last_name = $("#last_name").val();
var email_address = $("#email_address").val();
var message = $("#message").val();
var recaptcha_response_field = $("#recaptcha_response_field").val();
var recaptcha_challenge_field = $("#recaptcha_challenge_field").val();
$.ajax({
url:"/contact",
type: "POST",
data: { first_name : first_name, 
last_name : last_name, 
email_address : email_address, 
message : message,
recaptcha_challenge_field : recaptcha_challenge_field,
recaptcha_response_field : recaptcha_response_field },
dataType: "json",
success:function(data, textStatus, jqXHR){
...
},
error:function(jqXHR, textStatus, errorThrown){
...
}
});
So that was it. Now it all works fine. Take that bots! (shouldn't egg on the spammers, they're way better programmers than I am and now they're gonna attack me).
Hope that helps out some peeps.

3 comments:

Unknown said...

Very informative and well written post! Quite interesting and nice topic chosen for the post.
CyberPowerPC Laptops

Anonymous said...

Thank you for the post, it helped me figure out how to get the verify_recaptcha method working with our AJAX call. FYI, for anyone else using v2 of Recaptcha, pass params['g-recaptcha-response'] instead of params[:recaptcha_challenge_field] and params[:recaptcha_response_field].

Tejuteju said...

awesome post presented by you..your writing style is fabulous and keep updated with your blogs
Ruby on Rails Online Course Bangalore