Migrating to Google’s NoCaptcha ReCaptcha

Here at SendSafely we’ve been using ReCaptcha for a while now to protect unauthenticated portions of our website that may be susceptible to abuse by bots (the most notable of which is our Personal URL feature).

This month we upgraded all of our ReCaptchas to the new and improved “NoCaptcha ReCaptcha” API (officially known as ReCaptcha v2). Migrating existing ReCaptcha’s to the new v2 API was surprisingly easy. Unfortunately, our ReCaptcha code is not something we look at all that often, so we first needed to re-familiarized ourselves with the v1 implementation. As it turned out, we only needed to change a few lines of code to make our old code work with the new API. We thought we would post a summary of the steps needed to help those that haven’t made the switch but are thinking about it.  

A Refresher on ReCaptcha v1

Back when we originally implemented ReCaptcha, we opted not to use any of the third party libraries that mask the details of how ReCaptcha works. Implementing ReCaptcha typically involves writing two separate pieces of code within the application: the Rendering Logic and the Validation Logic. 

Rendering Logic

The rendering logic is the place in your code where the ReCaptcha widget is rendered. Rendering can be done in one of two ways:  Automatic Rendering or Explicit Rendering. 

Automatic Rendering 

With automatic rendering, you embed a single JavaScript element directly in your page at the spot where you want to include the widget as follows:

 

<script src="http://www.google.com/recaptcha/api/challenge?k=your_public_key"<</script>

 

Explicit Rendering

Explicit rendering lets you dynamically render the ReCaptcha using client-side JavaScript logic.  You need to load the Recaptcha Ajax library (http://www.google.com/recaptcha/api/js/recaptcha_ajax.js) and define an element in your HTML page where you want to place the rendered ReCaptcha box. Then you just call Recaptcha.create(your_public_key, element_id) when you want to render the ReCaptcha.  

Validation Logic

The validation logic is the place in your code that handles validation of the user’s response. This is generally code that runs when the user clicks on the “Submit” button. It’s hard to generalize about what this code looks like in a custom implementation but, but the code needs to make a call to Google to verify the user’s response. Typically this code will either:

  • Read the recaptcha_challenge_field and recaptcha_response_field form inputs. These two inputs are generated automatically by the rendering logic, so their use is generally required.  
  • Call Recaptcha.get_challenge() and Recaptcha.get_response().  These two methods  return the “recaptcha_challenge_field” and “recaptcha_response_field” values generated by the rendering logic.

On the server-side, your code then takes the challenge and response values and validates them by making a back-end call to Google to figure out whether the challenge succeeded or not.  

Conversion to v2

In order to upgrade to the v2 API, you’ll need to edit both the rendering and validation logic. Luckily the required changes are minimal.  

Rendering Logic

As with the v1 API, you can render the new NoCaptcha ReCaptcha either automatically or explicitly. Depending on which method you use, the steps to migrate will vary. In either case your page needs to include a single ReCaptcha JavaScript file (https://www.google.com/recaptcha/api.js), which is the same for both methods of rendering.  

Automatic Rendering

The v2 API requires you to define a placeholder element in your HTML page where you want the widget rendered. Previously, this was only required if doing explicit rendering. The placeholder element needs to have a class of “g-recaptcha” and uses an HTML data element (data-sitekey) to pass the API key to the JavaScript.  <br>When your page includes the api.js file, the script looks for an element with the “g-recaptcha” class, reads the data-sitekey attribute, and insert the widget in its location. You should basically place this element in the exact same location that you previously had the script tag that used to load http://www.google.com/recaptcha/api/challenge.  

Automatic Rendering Example Before

<html>
 <head>
   <title>reCAPTCHA Demo</title>
 </head>
 <body>
  <form action="?" method="POST">
     <script type="text/javascript” src="http://www.google.com/recaptcha/api/challenge?k=your_public_key">
     </script>
     <input type="submit" value="Submit">
  </form>
 </body>
</html>

 

Automatic Rendering Example After

<html>
 <head>
   <title>reCAPTCHA Demo</title>
    <script src="https://www.google.com/recaptcha/api.js" async defer></script>
 </head>
 <body>
   <form action="?" method="POST">
     <div class="g-recaptcha" data-sitekey="your_site_key"></div>
     <input type="submit" value="Submit">
   </form>
 </body>
</html>

 

Explicit Rendering

Explicitly rendering the widget with the v2 API is very similar to how it was done with v1. In most cases you’ll just need to change the reference to the .js file and replace the call to Recaptcha.create() with one to grecaptcha.render().  

Explicit Rendering Example Before

<html>
 <head>
   <title>reCAPTCHA Demo</title>
    <script type="text/javascript" src="http://www.google.com/recaptcha/api/js/recaptcha_ajax.js"></script>
    <script>
Recaptcha.create("your_public_key", "recaptcha_div");
    </script>
 </head>
 <body>
   <form action="?" method="POST">
     <div id="recaptcha_div"></div>
     <input type="submit" value="Submit">
   </form>
 </body>
</html>

 

Explicit Rendering Example After

<html>
 <head>
   <title>reCAPTCHA Demo</title>
         <script src="https://www.google.com/recaptcha/api.js?render=explicit" async defer></script>
    <script>
function generateCaptcha()
{
        grecaptcha.render(‘recaptcha_div’, { 'sitekey' : 'your_site_key'});
}
    </script>
 </head>
 <body>
   <form action="?" method="POST">
     <div id="recaptcha_div"></div>
     <input type="submit" value="Submit">
   </form>
 </body>
</html>

 

Validation Logic

With the v2 API, you no longer have to validate the user’s response directly. Instead, the widget returns a token to your page once the user has been verified as human.  The token is then validated with Google to ensure its authenticity. Similar to the previous version, the token is passed to your page via a hidden field (g-recaptcha-response) that gets inserted automatically by the widget when it loads. The recaptcha_challenge_field and recaptcha_response_field form inputs are no longer inserted by the v2 API, so any reference to those inputs should be removed.

Validation Example Before

URL url = new URL("http://www.google.com/recaptcha/api/verify");

URLConnection urlConn = url.openConnection();

urlConn.setDoOutput(true);

urlConn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");

DataOutputStream cgiInput = new DataOutputStream(urlConn.getOutputStream());

String content = "privatekey=" + your_private_key + "&remoteip=" + request.getRemoteAddr() + "&challenge=" + request.getParameter(‘’) + "&response=" + request.getParameter(‘’);

cgiInput.writeBytes(content);

cgiInput.flush();

cgiInput.close();

BufferedReader cgiOutput =  new BufferedReader(new InputStreamReader(urlConn.getInputStream()));

boolean success = false;

if (cgiOutput.readLine().trim().toLowerCase().equals("true"))
{
//success
}
else
{
//failure
}

cgiOutput.close();

 

Validation Example After

URL url = new URL("https://www.google.com/recaptcha/api/siteverify?secret=" + your_private_key + "&response=" + request.getParameter(‘’) + "&remoteip=" + request.getRemoteAddr());

HttpURLConnection con = (HttpURLConnection) url.openConnection();

con.setRequestMethod("GET");

BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));

String inputLine;

StringBuffer responseBuffer = new StringBuffer();

while ((inputLine = in.readLine()) != null) {
responseBuffer.append(inputLine);
}

in.close();

String response = responseBuffer.toString();

if (response.contains("true"))
{
//success
}
else
{
//failure
}


That’s it! Hopefully this walkthrough will be helpful for those (like us) that were somewhat rusty on ReCaptcha going into the migration.      

Topics: Engineering