Validating reCaptcha with jQuery and AJAX
I recently did some work for one of my clients that involved securing some simple web forms. These forms were to request literature or request a call back, simple stuff. Incidentally they were getting hammered with spam. The clients didn’t seem to care for this so they asked me to stop it.
My first move was to setup some sort of Captcha. I settled on reCaptcha and got to work on implementing it. I’d be on a bit of a jQuery kick lately so I wanted to see if I could validate the Captcha fields dynamically and only submit the form if they matched. It turned out that this was much easier than I’d thought.
To make this work, we’ll be using the following files:
- recaptcha.php – The page that holds our form
- ajax.recaptcha.php – The file that handles the ajax requests and outputs the result
Prerequisites
- The first thing we’ll do is setup our form. I’m going to assume you already have your form in place and that you are merely trying to secure it.
- Next, you’ll want to download the newest version of jQuery. You can put this file wherever you normally store your js files and include this in your page.
- Now it’s time to get yourself a reCaptcha keyset. A keyset is nothing more than a public and private key that allows you to use the reCaptcha service. It’s free. Head on over to http://recaptcha.net and click “Get reCaptcha” and fill out the necessary fields. Under domain, either fill out your site’s domain or set this up as a global key if you plan on using reCaptcha on multiple domains. Make a note of your public and private keys.
Now it’s time to put this all together. First we have to get our form to display our reCaptcha challenge field so the user can actually submit the form. My site uses PHP so I’ve downloaded the reCaptcha PHP Library and stored it on my server. At the top of my page, I have included this PHP file and setup my public and private keys. On this page, you really only need the public key, but I went ahead and included both so I don’t lose one of them. Here is what the top of my recaptcha.php looks like (with my keys blanked out):
recaptcha.php
require_once('/inc/recaptchalib.php'); $publickey = "XXXXXXXXXXXXXXXXXXXXXXXXXXXX"; // you got this from the signup page $privatekey = "XXXXXXXXXXXXXXXXXXXXXXXXXXXX";
Next, you will include this in your page where you want the reCaptcha box to appear in the form:
recaptcha.php
echo recaptcha_get_html($publickey);
Next, we need to write our function that handles the dynamic/AJAX reCaptcha validation for us. I’ve done that already and you can see it below:
recaptcha.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | function validateCaptcha() { challengeField = $("input#recaptcha_challenge_field").val(); responseField = $("input#recaptcha_response_field").val(); //alert(challengeField); //alert(responseField); //return false; var html = $.ajax({ type: "POST", url: "ajax.recaptcha.php", data: "recaptcha_challenge_field=" + challengeField + "&recaptcha_response_field=" + responseField, async: false }).responseText; if(html == "success") { $("#captchaStatus").html(" "); // Uncomment the following line in your application return true; } else { $("#captchaStatus").html("Your captcha is incorrect. Please try again"); Recaptcha.reload(); return false; } } |
The above function grabs the reCaptcha data, submits it via a POST request to a file called ajax.recaptcha.php on our server, and waits for the repsonse. When it gets the response it either updates the status telling your user that the captcha didn’t match and then it reloads a new captcha for them, or it submits the form. The running demo code is slightly different to prevent the form from actually being submitted.
The last step to this part is to make sure your form runs this function when the user clicks that submit button. To do this, update the form’s opening tag and add an onsubmit event like this:
recaptcha.php
form name="loginform" id="loginform" action="#" method="post" onSubmit="return validateCaptcha()"
You’ll notice that we return true or false from the validateCaptcha() function. This controls if the form is submitted or not and is important for making this work.
Our final step in this process is to build the ajax.recaptcha.php file that we mentioned earlier to handle the requests. I’ve done this already and here is the final product (again with my keys blanked out):
ajax.recaptcha.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | require_once('/inc/recaptchalib.php'); $publickey = "XXXXXXXXXXXXXXXXXXXXXXXXXXXX"; // you got this from the signup page $privatekey = "XXXXXXXXXXXXXXXXXXXXXXXXXXXX"; $resp = recaptcha_check_answer ($privatekey, $_SERVER["REMOTE_ADDR"], $_POST["recaptcha_challenge_field"], $_POST["recaptcha_response_field"]); if ($resp->is_valid) { ?>success< ? } else { die ("The reCAPTCHA wasn't entered correctly. Go back and try it again." . "(reCAPTCHA said: " . $resp->error . ")"); } |
This file outputs only the word “success” if the captcha matches and a message and the response from reCaptchta if it fails. This is important because we are looking for the word success in our validateCaptcha() function.
You can see the functioning demo for this or download a zip file of the files used in the demo including full PHP source code.
15 Dec 2008 04:12 pm Chris 32 comments
Hi,
thank you for posting this… it has proven to be very insightful.
Unfortunatley making sync calls from firefox just freezes it up.. any ideas on how to get around it?
works fine in IE!
Hyder,
I dropped you an email, but if you could provide me a link to your form I’d be glad to look at it.
Hi,
I tried the same example on a different machine(IIS 6.0) and it all worked fine so I thought it must be local to my machine (IIS 7 Vista x64). I upgraded my firefox to the latest version (3.x) and it all works fine
Thanks for your email
Regards,
Hyder
Just wanted to note, I couldn’t get this to work using & for the ajax code, I could only get & to work.
that is &(nospace)amp; vs &
Thanks for posting.
I’ve adapted the php code for ASP.NET and C#:
Recaptcha.RecaptchaValidator v = new Recaptcha.RecaptchaValidator();
v.RemoteIP = Request.ServerVariables["REMOTE_ADDR"];
v.PrivateKey = “XXXXXXXXXXXXXXXXXXXXXXXXXX”;
v.Challenge = Request["recaptcha_challenge_field"];
v.Response = Request["recaptcha_response_field"];
Recaptcha.RecaptchaResponse r = v.Validate();
Response.Write(r.IsValid);
It returns True or False in the html.
Nice tutorial! Thank you.
jQuery is all about unobtrusive javascript as far as im concerned. So shouldn’t the onsubmit be inside the javascript?
Like so:
$(function(){
function validateCaptcha(){ … };
$(“form”).submit(function(){
return validateCaptcha();
});
});
This way you don’t have to meddle with anything in the HTML part of the code. Just a small quirk just thought I’d point it out to others.
Good work!!
One little problem which is probably not caused by your solution:
typing the first word of the challenge returns success. No need to type both words. Does anyone else has this problem, too?
Hans.
Great work..
I’m also facing the same problem as Hans van Domselaar..
typing the first word of the challenge returns success..
If anybody have found the solution then please post it..
Thanks,
Mihir
hey there,
thanks for posting this, definitely a necessary part to using the re-captcha plugin. however i’m running into a problem with the this version of the captcha. it refreshes great if the captcha words are entered in wrong, but if they are correct it still supplies an error message. I un-commented the return true value that it says to do in the javascript, but still no luck.
And I set it up with the regular (non ajax/js) PHP code and it worked fine, so it’s not an issue with the public/private key values.
Any idea why this isn’t working?
Thanks!
@BeeDev You should be able to shuffle the JS around wherever you like and still have it work.
@Hans/Mihir I’m going to look at this and see if I can tell what is causing the issue.
@KM I’ll look at that too.
I’m having the same problem as KM. No matter what, it thinks that the recaptcha is incorrect. Any thoughts?
Sorry for double commenting but I’ve narrowed the issue down to the javascript code. If I direct the form to ajax.recaptcha.php and remove the onclick then the recaptcha works. It displays the “success” message if recaptcha is typed in correctly and it displays my error message if it is not. So, it seems that the problem is somewhere in the validateCaptcha javascript function.
function validateCaptcha()
OK, solved my problem. It only works if you use “&” instead of “&” in the recaptch.php file.
Just as Brandon said above, thats &(nospace)amp; doesn’t work, it needs to just be “&”. Maybe both ways work depending on your web host’s configuration, but again only “&” makes the code work for me.
@hans/mihir – it’s supposed to work that way, the first word is an actual word, the second word is one that has not been verified yet, but by typing it in, you are helping to verify it.
I am working on implementing this solution now, as I need a way to display multiple captchas on a page.
@Chris I wasn’t clear on this until recently.
Anyone using this should keep an eye on possible changes since Google has now acquired reCaptcha. I’m going to refresh this every few months or so just to make sure it still works.
Hi,
I’m not sure about this, but wouldn’t this require to validate the reCaptcha twice..
If some kind of attacker would like to avoid the captcha, the only thing he would have to do is create his own post data with a name field and send it to the script… If then there is no revalidation or any kind of saving mechanism for the ajax results, the data would just go through.
Or am I missing something there?
@Dominik
If you don’t properly secure your form yes. This tutorial doesn’t go into detail on methods you should be using to ensure that form data can only be sent from pages you’ve created. It merely touches on a way to validate captcha without having to handle prefilling form data for your users by using an ajax call.
Great article, but … it seems that recaptcha_check_answer() will only return valid once – if I call it a second time with the same data, it returns is_valid = false.
This makes client-side then server-side validation impossible!
Has reCaptcha changed something? Or am I missing something?
I think I was missing something … are you doing any server-side validation? How does the validation work if JavaScript is off?
Thanks, Chris
Using the above, I get the correct response, but my form doesn’t submit.
See: http://stackoverflow.com/questions/1681061/jquery-form-validation-problem
For more details.
The fix from Stack Overflow (link above). Was to make sure all other characters were stripped in JS:
if (html.replace(/^\s+|\s+$/, ”) == “success”)
Chris did you manage to get the server validation working after the client side validation, i have both working but not together, any help would be appreciated.
I’m unsure what the problem is, its almost as if the values are removed after the call is made?
Thanks
Derek
Why use:
$(“input#recaptcha_challenge_field”).val();
$(“input#recaptcha_response_field”).val();
When reCapcha AJAX API exposes:
Recaptcha.get_challenge()
Recaptcha.get_response()
?
Derek / Chris,
You can’t verify the same the same challenge/response more than once.
I think the documentation kind of mentions it here (second to last paragraph under “reCAPTCHA AJAX API”):
http://recaptcha.net/apidocs/captcha/client.html
What I’m going to do is handle the form operation in the same AJAX call as the captcha verification. That way I only need to verify it once and there’s no way to bypass captcha by messing with the request.
You are a life saver. I had the same problem with using jquery validate and the remote call — I wanted the result to send back true or false and either reload the recaptcha or recognize that it was filled out correctly. That worked, but then the form wouldn’t submit. Used your tip to finish it off. The form is in action at http://www.ontaponline.com/register.
If anyone wants the full code just let me know and I can post it or email it.
Cheers.
I got the recaptcha thing to work .. but its not submitting the form ? it just says … Success. Submitting form.
how do i get it to actually submit the form ?
@Alex
When I wrote this, reCaptcha didn’t have their own exposed AJAX API methods so I had to use alternate methods. I have finally cleared enough off my table at work to redo this article with a current version of reCaptcha.
@Chris
will you be posting the article that works with a current version of reCaptcha and incorporates all the comments and stuff? thanks
@nesh I will indeed.
Hello
I see that you use ajax.recaptcha.php to validate if a captcha is correct and then if it is, it is submited
But when someone hacks the page, he can force it to just post it anyway.
So I would like a more secure solution, that the check is done on the server and when the check is successful the filled in form is send to the database or whatever has to happen
So everything including the filled in form and captcha is send to the server.
So far as I can see this is not 100% waterproof
Could you please update me on this, since I’m looking for a solid solution to integrate a captcha in a jquery modal dialog.
gr
Jan