reCAPTCHAv3 Explored

I am breaking my own rule temporarily and this is not a static site for the moment. For a proof of value, I am demoing some of the insights you can gain simply by using reCAPTCHAv3.

Suppose I have a form that makes an async CORS request requiring some level of trust. In this example I am invoking an AWS Lambda endpoint that does not trust the users clicking this button. Perhaps they are bots, perhaps not, but with reCAPTCHAv3 all I need to do is load the Google scripts with my site key.

Load the JavaScript.

 <script src="https://www.google.com/recaptcha/api.js"></script>

Add some callbacks.

<script>
  function  privRequest(token) {
     console.log(token)
     $.get(
       "https://zd6em4b0te.execute-api.us-west-2.amazonaws.com/default/mySecretMood", 
       { token: token }, 
       (res) => 
         $('#test').html(`
           <h4>Token</h4>
           <code>${token}</code>
           <h4>Site inspection result (seen server-side)</h4>
           <code>${res.body}</code>
         `) 
      )
  }
</script>

Add some attributes to the button element.

<button 
  class="g-recaptcha button" 
  data-sitekey="6Le7J-gZAAAAAIFTGsv88nwaRjo6iZOmjb-uiG1p" 
  data-callback='privRequest' 
  data-action='submit'>Submit
</button>

The Google docs are pretty straightforward on how to generate the client and server (secret) key pairs.

Once you’ve clicked the button above, you’ll see some interesting stuff from both a client and server perspective. For this demo, I’m simply returning the value of the server-side introspection from my super sensitive endopint that doesn’t want bots.

So what are we achieving here? Firstly , we are automatically geneating a CSRF token to prevent replays. A single click is a single action is a single token.

My Lambda is very simple.

const got = require('got')


exports.handler = async(event, context) => {

    console.info(event);
    const captchaSecret = [redacted];
    const captchaResponse = event.queryStringParameters.token;
    const captchaIp = event.requestContext.identity.sourceIp;
    const captchaUrl = 'https://google.com/recaptcha/api/siteverify';

    const params = {
        secret: captchaSecret,
        response: captchaResponse,
        remoteip: captchaIp
    };

    const { body } = await got.post(captchaUrl, {
        searchParams: params
    });

    const response = {
        statusCode: 200,
        headers: {
            'Content-Type': 'application/json',
            'Access-Control-Allow-Origin': 'https://wh.itesi.de'
        },
        body: JSON.stringify({ body }),
    };
    return response;

};

You can see that all it really does is perform the introspection of the client-side token calling Google’s siteverify endpoint. Google is automatically assigning my unique client token a trust value in a range of (0.0 - 0.9).

Now suppose that I want to action upon such data. My Lambda function could be modified to, for example, alert somebody or return a 403 response (forbidden) if the trust score falls below 0.5.