45

I am doing a Java (JSP + Servlet) web application (I understand that this question is technology-independent). I hope to use the latest Google reCAPTCHA service.

I am playing with a Google reCAPTCHA example found here:

https://developers.google.com/recaptcha/docs/display#config

<html>
  <head>
    <title>reCAPTCHA demo: Simple page</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="my_site_key"></div>
      <br/>
      <input type="submit" value="Submit">
    </form>
  </body>
</html>

I am able to see the displayed recaptcha image as follows:

enter image description here

When I check "I'm not a robot", I get the following:

enter image description here

As you can see, there is a Verify button and based on my tests, user response is sent to Google for verification.

How can I get the user response so that I can verify user response in my own backend code (as suggested by Google at https://developers.google.com/recaptcha/docs/verify).

g-recaptcha-response POST parameter when the user submits the form on your site

On the server side, I can, by clicking on the "Submit" button, get user input from parameter "g-recaptcha-response" only when a user is verified successfully with Google first. Otherwise, "g-recaptcha-response" is blank on the server side. This means that I can do server-side verification only after the client-side's verification success. If so, what is the point of doing another verification on the server-side, which is the option provided by Google reCAPTHA?

Do I miss anything?

simhumileco
  • 31,877
  • 16
  • 137
  • 115
curious1
  • 14,155
  • 37
  • 130
  • 231

4 Answers4

121

The cool thing about the new Google Recaptcha is that the validation is now completely encapsulated in the widget. That means, that the widget will take care of asking questions, validating responses all the way till it determines that a user is actually a human, only then you get a g-recaptcha-response value.

But that does not keep your site safe from HTTP client request forgery.

Anyone with HTTP POST knowledge could put random data inside of the g-recaptcha-response form field, and fool your site to make it think that this field was provided by the google widget. So you have to validate this token.

In human speech, it would be like,

  • Your Server: Hey Google, there's a dude that tells me that he's not a robot. He says that you already verified that he's a human, and he told me to give you this token as proof of that.
  • Google: Hmm... let me check this token... yes I remember this dude I gave him this token... yeah he's made of flesh and bone let him through.
  • Your Server: Hey Google, there's another dude that tells me that he's a human. He also gave me a token.
  • Google: Hmm... it's the same token you gave me last time... I'm pretty sure this guy is trying to fool you. Tell him to get off your site.

Validating the response is really easy. Just make a GET request to

https://www.google.com/recaptcha/api/siteverify?secret=your_secret&response=response_string&remoteip=user_ip_address

And replace the response_string with the value that you earlier got by the g-recaptcha-response field.

You will get a JSON Response with a success field.

More information here: https://developers.google.com/recaptcha/docs/verify

Edit: It's actually a POST, as per documentation here.

RanRag
  • 48,359
  • 38
  • 114
  • 167
TheRueger
  • 2,508
  • 1
  • 16
  • 12
  • 2
    TheRueger, thanks so much for your input! I read all what you mentioned here before making this post. The Google reCAPTCHA indicates that that a server-side validation is one of the verification approaches. Am I right? What do you mean by "You just have to ask google if the g-recaptcha-response is valid or not.". I am confused by the two steps "validation is done by googles widget automatically" and then "You will get a JSON Response with a success field". If the first step is a success, why the second step? Regards – curious1 Dec 05 '14 at 16:11
  • Hey @curious1, the reason why the second step is needed is easy. All input from a browser is potentially faked. What I mean is that you cannot validate a users input only on the client side so google is validating the user as a human but he cannot just send you a hidden field named "UserIsHuman" true/false. That could easily be faked. So google is validating the user and storing the result under an id and that allows you to ask google if that id is is a human or not within your backend. See also [link](http://stackoverflow.com/questions/3614467/pure-javascript-jquery-html-captcha) – TheRueger Dec 08 '14 at 07:55
  • TheRueger, thanks for your updated answer and follow-up. I understand what you said. However, I feel this would be more efficient. Step 1: when the form and recaptcha image is displayed, Google stores the encrypted string (of the letters or digits in the recaptcha image). Step 2: When the form is submitted, the server-side code gets user response and the encrypted string, and sends them to Google for verification. This is only one trip to Google for verification. What do you think? – curious1 Dec 08 '14 at 15:13
  • Do you happen to know the answer to my another post http://stackoverflow.com/questions/27323863/google-recaptcha-show-only-digits-in-recaptcha-image ? Thanks!!! – curious1 Dec 08 '14 at 15:15
  • 12
    If you're making a GET request to `.../siteverify` you're putting the **private key** in the query string? How can that be considered a private key at that point? – Yuck Dec 01 '15 at 13:12
  • 2
    @yuck read here: http://stackoverflow.com/questions/323200/is-an-https-query-string-secure – TheRueger Apr 18 '16 at 13:44
  • 13
    @TheRueger The request to [https://www.google.com/recaptcha/api/siteverify](https://www.google.com/recaptcha/api/siteverify) shouldn't be a GET request, but a POST request. See [https://developers.google.com/recaptcha/docs/verify](https://developers.google.com/recaptcha/docs/verify). – Aram Dec 06 '17 at 13:14
  • @TheRueger providing the same g-recaptcha response wont grant you access – ghost21blade Jul 04 '21 at 14:14
  • The token Verification needs to be done from the backend. – rii Mar 23 '23 at 17:50
12

A method I use in my login servlet to verify reCaptcha responses. Uses classes from the java.json package. Returns the API response in a JsonObject.

Check the success field for true or false

private JsonObject validateCaptcha(String secret, String response, String remoteip)
{
    JsonObject jsonObject = null;
    URLConnection connection = null;
    InputStream is = null;
    String charset = java.nio.charset.StandardCharsets.UTF_8.name();

    String url = "https://www.google.com/recaptcha/api/siteverify";
    try {            
        String query = String.format("secret=%s&response=%s&remoteip=%s", 
        URLEncoder.encode(secret, charset), 
        URLEncoder.encode(response, charset),
        URLEncoder.encode(remoteip, charset));

        connection = new URL(url + "?" + query).openConnection();
        is = connection.getInputStream();
        JsonReader rdr = Json.createReader(is);
        jsonObject = rdr.readObject();

    } catch (IOException ex) {
        Logger.getLogger(Login.class.getName()).log(Level.SEVERE, null, ex);
    }
    finally {
        if (is != null) {
            try {
                is.close();
            } catch (IOException e) {
            }

        }
    }
    return jsonObject;
}
Rob Kraft
  • 216
  • 3
  • 6
  • I keep getting this error: java.lang.ClassCastException: javax.json.JsonValue$2 cannot be cast to javax.json.JsonString every single time I try and parse "success" from the return value, JsonObject. Here is the actual call that throws the error, any thoughts? JsonString response = jsonObject.getJsonString("success"); – logixplayer Jun 21 '15 at 19:57
  • This command also failed: String response = jsonObject.getJsonString("success"); I ended up doing this: String returnValue = jsonObject.getOrDefault("success", null).toString(); not happy with this, ugly and unelegant .Spent 3 hours going nuts over this. – logixplayer Jun 21 '15 at 20:45
  • Try Boolean success = jsonObject.getBoolean("success")) – Rob Kraft Jun 22 '15 at 18:00
4

Hi curious you can validate your google recaptcha at client side also 100% work for me to verify your google recaptcha just see below code
This code at the html body:

 <div class="g-recaptcha" id="rcaptcha" style="margin-left: 90px;" data-sitekey="my_key"></div>
 <span id="captcha" style="margin-left:100px;color:red" />

This code put at head section on call get_action(this) method form button:

function get_action(form) {

var v = grecaptcha.getResponse();
if(v.length == 0)
{
    document.getElementById('captcha').innerHTML="You can't leave Captcha Code empty";
    return false;
}
 if(v.length != 0)
 {
    document.getElementById('captcha').innerHTML="Captcha completed";
    return true; 
 }
}
Malachi
  • 3,205
  • 4
  • 29
  • 46
Pravin Sharma
  • 1,160
  • 1
  • 8
  • 20
  • 3
    If I understand you correctly you want to validate the captcha on client side only! Shur you _can_ validate on client side, the question is then the need of a captcha at all? The reason why captchas are validated in the backend is, that the frontend can be manipulated or simulated by a stupid bot that then, for example creates users and spams your application. – TheRueger May 06 '15 at 07:03
  • try server site here : https://stackoverflow.com/questions/31354633/curl-recaptcha-not-working-php – Anthony Kal Aug 16 '17 at 02:50
  • @TheRueger isn't that the case anyhow? I don't understand how it works, correct me if I'm wrong, you verify the token on backend and then return it to client side again where you'll check whether the form may be submitted or not. This last check can be manipulated aswell right? A bot could just get rid of the last check before sending the form, or am I wrong? – Omar Ruder Nov 10 '20 at 08:03
  • 1
    @OmarRuder The frontend should only checking the response of the captcha in first place to inform the user. But the response of the captcha validation must be included in the form submitted. The backend must then ask google again if the captcha key received from the frontend is correct. Only then the backend must accept the form at all. – TheRueger Nov 11 '20 at 11:17
4

Here is complete demo code to understand client side and server side process. you can copy paste it and just replace google site key and google secret key.

<?php 
if(!empty($_REQUEST))
{
      //  echo '<pre>'; print_r($_REQUEST); die('END');
        $post = [
            'secret' => 'Your Secret key',
            'response' => $_REQUEST['g-recaptcha-response'],
        ];
        $ch = curl_init();

        curl_setopt($ch, CURLOPT_URL,"https://www.google.com/recaptcha/api/siteverify");
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($post));
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

        $server_output = curl_exec($ch);

        curl_close ($ch);
        echo '<pre>'; print_r($server_output); die('ss');
}
?>
<html>
  <head>
    <title>reCAPTCHA demo: Explicit render for multiple widgets</title>
    <script type="text/javascript">
      var site_key = 'Your Site key';
      var verifyCallback = function(response) {
        alert(response);
      };
      var widgetId1;
      var widgetId2;
      var onloadCallback = function() {
        // Renders the HTML element with id 'example1' as a reCAPTCHA widget.
        // The id of the reCAPTCHA widget is assigned to 'widgetId1'.
        widgetId1 = grecaptcha.render('example1', {
          'sitekey' : site_key,
          'theme' : 'light'
        });
        widgetId2 = grecaptcha.render(document.getElementById('example2'), {
          'sitekey' : site_key
        });
        grecaptcha.render('example3', {
          'sitekey' : site_key,
          'callback' : verifyCallback,
          'theme' : 'dark'
        });
      };
    </script>
  </head>
  <body>
    <!-- The g-recaptcha-response string displays in an alert message upon submit. -->
    <form action="javascript:alert(grecaptcha.getResponse(widgetId1));">
      <div id="example1"></div>
      <br>
      <input type="submit" value="getResponse">
    </form>
    <br>
    <!-- Resets reCAPTCHA widgetId2 upon submit. -->
    <form action="javascript:grecaptcha.reset(widgetId2);">
      <div id="example2"></div>
      <br>
      <input type="submit" value="reset">
    </form>
    <br>
    <!-- POSTs back to the page's URL upon submit with a g-recaptcha-response POST parameter. -->
    <form action="?" method="POST">
      <div id="example3"></div>
      <br>
      <input type="submit" value="Submit">
    </form>
    <script src="https://www.google.com/recaptcha/api.js?onload=onloadCallback&render=explicit"
        async defer>
    </script>
  </body>
</html>
Sandeep Sherpur
  • 2,418
  • 25
  • 27