Episode 15: Using a CAPTCHA in your Google App Engine Application

Welcome to Episode 15. In this episode, we shall cover how to incorporate a CAPTCHA in your application. The CAPTCHA solution that I will be demonstrating over here is the ReCAPTCHA project found here. To quote from its Wikipedia entry, a CAPTCHA is a type of challenge-response test used in computing to ensure that the response is not generated by the computer. Usually, one sees one or two words shown to us that we need to enter and they need to match before our request submission is accepted by the server.

See it in Action

 

Let us first see the application that we shall be developing in its final form. The main screen is a dummy form that accepts one field and displays the CAPTCHA to the user as shown below:

If the CAPTCHA response is entered correctly and the submit button is clicked, the response is sent to the server. The server side validates the CAPTCHA challenge-response and will display an “all is well” message as shown below:

If the CAPTCHA response is not entered correctly, the server side will display an “all is not well” message as shown below:

ReCAPTCHA Project

 

We shall be using the ReCAPTCHAProject present at http://recaptcha.net/. The ReCAPTCHAproject is comprehensive and provides support for a variety of server side platforms, hence I have gone with it. I do not have a particular preference for it, except that most people recommended this to me and hence I decided to try it out. I was able to integrate it easily and quickly into a recent App Engine application that is currently live, so I can vouch for its ease of integration and which is what I shall demonstrate in this experiment.

To get started with ReCAPTCHA, we need to do 2 things:

1. Get your private/public keys for the site name hat your application will be running on.

 

Follow these steps:

  1. First get yourself a ReCAPTCHAaccount. Go to http://recaptcha.net/whyrecaptcha.html and click on the Sign up Now! button.
  2. This will bring up a login form, but you click on the “New to reCaptcha? Sign up” link. Here you can register by giving a username/password/email . Click on the Sign up now button.
  3. On successful registration, you will signed in automatically and led to a Create a reCAPTCHA key page. Here you need to enter the site name that you will be using ReCAPTCHAon. Remember you can get ReCAPTCHAkeys for more than 1 site and you can manage it all from your account itself. For e.g. you can get a key for localhost since that will be what we need to test on first before deploying to the Google App Engine cloud. And then you will atleast need one based on the Application ID of your Google App Engine application. So for e.g. if my application ID is gaejexperiments, then my full site url is http://gaejexperiments.appspot.com. So you will need to enter gaejexperiments.appspot.com in the Domain form field.
  4. Click on Create Key. For e.g. shown below is the series of screens that I got when I wanted keys for localhost. You will need to do the same if you wish to test the code first on your local system i.e. localhost.

 

Then when you click Create Key, you will be presented the Public Key and Private Key as shown below:

Please note down the Public and Private Key since you will need to use them in your application. The usage is straightforward and mentioned on the screen and I will be repeat it here:

  • Use the Public Key in your HTML page (Javascript) to communicate with the ReCAPTCHA server to get the challenge (image).
  • Use the Private Key in your server side Java code that will be used to communicate with the Server to verify the challenge + response submitted by the form in the above step.

2. Download the JAR file that we shall be using at the Server side to verify the CAPTCHA challenge and response.

 

To do the server side validation, you need to download a JAR  file that is available from the following url : http://code.google.com/p/recaptcha/downloads/list?q=label:java-Latest. Go to the URL and you shall be presented with a screen as shown below:

The JAR file is required and it encapsulates all communication between our application and the ReCAPTCHA Servers.

Download the zip file, expand it in  an appropriate folder and you will find the recaptcha4j-0.0.7.jar present in that.  We will need it later on to be referenced in your App Engine Eclipse Project.

Developing our Application

 

The first thing to do is to create a New Google Web Application Project. Follow these steps:

1. Either click on File –> New –> Other or press Ctrl-N to create a new project. Select Google and then Web Application project. Alternately you could also click on the New Web Application Project Toolbar icon as part of the Google Eclipse plugin.
2. In the New Web Application Project dialog, deselect the Use Google Web Toolkit and give a name to your project. I have named mine GAEJExperiments. I suggest you go with the same name so that things are consistent with the rest of the article, but I leave that to you. In case you are following the series, you could simply use the same project and skip all these steps altogether. You can go straight to the Servlet Development section.
3. Click on Finish.

This will generate the project and also create a sample Hello World Servlet for you. But we will be writing our own Servlet.

Add the recaptcha4j-0.0.7.jar file to your Project classpath

 

The recaptcha4j-0.0.7.jar file that we downloaded needs to be added to the runtime and compile time CLASSPATH of your App Engine Eclipse Project. Follow these steps:
a) Copy recaptcha4j-0.0.7.jar to the WEB-INFlib folder of your project. This folder is present in the war sub folder of your main project structure.
b) Go to Project properties –> Java Build Path and reference the JAR file (recaptcha4j-0.0.7.jar) in the Java Build Path of your application as shown below:

The Front end HTML form [captcha.html]

 

<html xmlns="<a href="http://www.w3.org/1999/xhtml">http://www.w3.org/1999/xhtml</a>" lang="en" xml:lang="en">
<head>
<title>ReCaptcha Integration</title>
<script type="text/javascript" src="<a href="http://api.recaptcha.net/js/recaptcha_ajax.js%22%3E%3C/script">http://api.recaptcha.net/js/recaptcha_ajax.js"></script</a>>
<script type="text/javascript">
function PreloadCaptcha() {
showRecaptcha();
}

function showRecaptcha() {
Recaptcha.create("YOUR_PUBLIC_KEY", "dynamic_recaptcha_1", {
theme: "white",
callback: Recaptcha.focus_response_field
});
}

var xmlhttp;
function submitFormData(name)
{

//alert("Message");
xmlhttp=null;
if (window.XMLHttpRequest)
{// code for IE7, Firefox, Opera, etc.
xmlhttp=new XMLHttpRequest();
}
else if (window.ActiveXObject)
{// code for IE6, IE5
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
if (xmlhttp!=null)
{
xmlhttp.onreadystatechange=state_Change;
var url = "postformdata";
var params = "name="+name+"&recaptcha_challenge_field=" + Recaptcha.get_challenge() + "&recaptcha_response_field="+Recaptcha.get_response();
var status = document.getElementById("status");
status.innerHTML = "<img src='img/ajax-loader.gif'><b>Submitting your data. Please wait...</b>";
xmlhttp.open("POST",url,true);
xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xmlhttp.setRequestHeader("Content-length", params.length);
xmlhttp.setRequestHeader("Connection", "close");
xmlhttp.send(params);
}
else
{
alert("Your browser does not support XMLHTTP.");
}
}

function state_Change()
{
if (xmlhttp.readyState==4)
{// 4 = "loaded"
if (xmlhttp.status==200)
{
// 200 = "OK"
var status = document.getElementById("status");
status.innerHTML = xmlhttp.responseText;;
Recaptcha.reload();
setTimeout(function() {
status.innerHTML = "";
}, 3000);
}
else {
var status = document.getElementById("status");
status.innerHTML = xmlhttp.responseText;;
Recaptcha.reload();
setTimeout(function() {
status.innerHTML = "";
}, 3000);
}
}
}
</script>
</head>

<body onload="PreloadCaptcha()">
<FORM NAME="dataform">
<TABLE>
<TR>
<TD><b>Your name:</b></TD>
<TD><INPUT NAME="txtName"/></TD>
</TR>
<TR>
<TD colspan="2"><div id="dynamic_recaptcha_1"></div></TD>
</TR>
<TR>
<TD colspan="2"><INPUT type="button" value="Submit Data" name="btnSubmitData" onClick="submitFormData(txtName.value); return true"></TD>
</TR>
<TR>
<TD colspan="2"><div id="status"/></TD>
</TR>
</TABLE>
</FORM>
</body>
</html>

 

Let us go through the important parts of the code:

1. The first part to notice is that I have referenced the javascript file for the ReCAPTCHA code as shown :

<script type="text/javascript" src="http://api.recaptcha.net/js/recaptcha_ajax.js"></script>

2. There is a javascript function call being made on the load of the body (<body onload=”PreloadCaptcha()”>). This invokes the function showRecaptcha() that is shown below:

 

Recaptcha.create("YOUR_PUBLIC_KEY", "dynamic_recaptcha_1", {
theme: "white",
callback: Recaptcha.focus_response_field
});

 

 

The Recaptcha class is available from the javascript file and we use the create method. The first parameter is the PUBLIC_KEY that you get when you registered at ReCaptcha for  your site name. If you are testing it locally, this will be the Public Key for your localhost site. The second parameter is a DIV element that is present in the form.

The create() method on successful invocation will populate the DIV element with the CAPTCHA challenge/response fields.

3. The rest of the code is standard AJAX stuff. The submit button invokes the submitDataForm() that does the following:

  • It will invoke the URL : /postformdata that will be our servlet that does the verification. The servlet code is discussed in the next section
  • It will form the request parameter string as shown below:
    var params = “name=”+name+”&recaptcha_challenge_field=” + Recaptcha.get_challenge() + “&recaptcha_response_field=”+Recaptcha.get_response();
  • Note the two fields : recaptcha_challenge_field and recaptcha_challenge_response_field. We get the two values from the Recaptcha class methods, get_challenge() and get_response() respectively. The get_challenge() is what was provided by the ReCAPTCHA Server and the get_response() is what was entered by the user.
  • Finally we do a POST to the server and collect the response. The response is that displayed in another DIV element “status”.

Please note that the code is just for demonstration purpose and may not represent the best practices or most efficient way of writing JavaScript, AJAX, etc.

The Servlet [PostFormDataServlet.java]

 

package com.gaejexperiments.captcha;

import java.io.IOException;

import javax.servlet.http.*;

//RECAPTCHA
import net.tanesha.recaptcha.ReCaptchaImpl;
import net.tanesha.recaptcha.ReCaptchaResponse;
@SuppressWarnings("serial")
public class PostFormDataServlet extends HttpServlet {

public void doPost(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
resp.setContentType("text/plain");
String strResponse = "";
try {

/*
* CAPTCHA CHECK
*
*
*/
//First validate the captcha, if not -- just get out of the loop
String challenge = (String) req.getParameter("recaptcha_challenge_field");
String response = (String) req.getParameter("recaptcha_response_field");
if ((challenge == null) || (response == null)) {
throw new Exception("Your words did not match. Please try submitting again.");
}

String remoteAddr = req.getRemoteAddr();
ReCaptchaImpl reCaptcha = new ReCaptchaImpl();

reCaptcha.setPrivateKey("YOUR_PRIVATE_KEY");

ReCaptchaResponse reCaptchaResponse =
reCaptcha.checkAnswer(remoteAddr, challenge, response);

if (!reCaptchaResponse.isValid()) {
//RECAPTCHA VALIDATION FAILED
throw new Exception("Your words did not match. Please try submitting again.");
}

strResponse = "Your record has been accepted and you did a good job entering the two words. Thank you";
}
catch (Exception ex) {
strResponse = "You record was not accepted. Reason : " + ex.getMessage();
}
resp.getWriter().println(strResponse);
}
}

Let us go through the important parts in the code:

1. Notice that we are importing  2 classes : net.tanesha.recaptcha.ReCaptchaImpl and net.tanesha.recaptcha.ReCaptchaResponse at the beginning. These are the implementation classes that encapsulate the verification with the ReCAPTCHA Server and the response from the ReCAPTCHA Server respectively.

2. We first extract out the challenge and the response fields as passed by our form.

 

String challenge = (String) req.getParameter("recaptcha_challenge_field");
String response = (String) req.getParameter("recaptcha_response_field");

 

3. We also need the Remote IP Address that is making the request.

 

String remoteAddr = req.getRemoteAddr();

 

4. We then instantiate an instance of the ReCaptchaImpl class. We set the Private Key that we got during our registration for the site localhost. Please use your key over here. And then we make a call to their server by invoking the checkAnswer method. The checkAnswer method takes 3 parameters : challenge, response and the Remote Address.

 

ReCaptchaImpl reCaptcha = new ReCaptchaImpl();

reCaptcha.setPrivateKey("YOUR_PRIVATE_KEY");

ReCaptchaResponse reCaptchaResponse =
reCaptcha.checkAnswer(remoteAddr, challenge, response);

 

5. We will receive an instance of the ReCaptchaResponse class. We simply use a utility method isValid() to determine if the response entered for the challenge was correct. And depending on that we send back an appropriate response back to the browser, which is then displayed to the user.

Servlet Configuration

To complete our Servlet development, we will also need to add the <servlet/> and <servlet-mapping/> entry to the web.xml file. This file is present in the WEB-INF folder of the project. The necessary fragment to be added to your web.xml file are shown below. Please note that you can use your own namespace and servlet class. Just modify it accordingly if you do so.

 

<servlet>
<servlet-name>PostFormDataServlet</servlet-name>
<servlet-class>com.gaejexperiments.captcha.PostFormDataServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>PostFormDataServlet</servlet-name>
<url-pattern>/postformdata</url-pattern>
</servlet-mapping>

 

Running the application locally

I am assuming that you have already created a new Google Web Application Project and have created the above Servlets, web.xml , etc. We shall be running this episode within our local development server only since the keys that I have used are for localhost. Before you deploy the application to the actual Google App Engine infrastructure using your Application ID, do remember to get yourself the appropriate public/private ReCAPTCHA keys for your site name.

So assuming that all is well, we will run our application, by right-clicking on the project and selecting Run As –> Web Application. Launch the browser on your local machine and navigate to http://localhost:<YourLocalPort>/captcha.html

Conclusion

In this episode, we saw how to incorporate a CAPTCHA in our Google App Engine application. Please remember that you will need to determine if you really need a CAPTCHA in your application. There are some good points mentioned in this article on various other techniques that can be used to address the same problems.

Read more Episodes on App Engine Services

 

About these ads

About rominirani

Google Developer Expert Cloud 2014. Harnessing the power of software by learning, teaching and developing simple solutions. I love learning about new technologies and teaching it to others.
This entry was posted in Cloud Computing, Google App Engine. Bookmark the permalink.

4 Responses to Episode 15: Using a CAPTCHA in your Google App Engine Application

  1. Pingback: uberVU - social comments

  2. Yonatan says:

    Thanks for the article!
    Just a minor clarification: You don’t need two different key sets for both your site and the development environment. Every reCaptcha key will be valid for localhost or 127.0.0.1 as well for the registered domain.

    http://recaptcha.net/apidocs/captcha/client.html

  3. Saqib Ali says:

    @rominirani : Thanks for putting this together. it was very helpful.

    FYI, the script tag for recaptcha_ajax in captcha.html is incorrect.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s