43

How to integrate Google reCAPTCHA Version 3 in Client Side and Server Side(php). following code use to display recaptcha but its not working good. How to do this integration.

<html>

<head>
  <script src='https://www.google.com/recaptcha/api.js?render=XXXX-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'></script>
</head>

<body>
  <script>
    grecaptcha.ready(function() {
      grecaptcha.execute('XXXX-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX', {
        action: 'action_name'
      });
    });
  </script>

  <form action="verify.php" method="post">
    <input type="text" name="name" placeholder="Your name" required>
    <input type="email" name="email" placeholder="Your email address" required>
    <textarea name="message" placeholder="Type your message here...." required></textarea>

    <input type="submit" name="submit" value="SUBMIT">

  </form>

</body>

</html>

Verify.php

<?php

    if(isset($_POST['g-recaptcha-response']) && !empty($_POST['g-recaptcha-response'])) {
        //your site secret key
        $secret = 'XXXX-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX';
        //get verify response data
        $verifyResponse = file_get_contents('https://www.google.com/recaptcha/api/siteverify?secret='.$secret.'&response='.$_POST['g-recaptcha-response']);
        $responseData = json_decode($verifyResponse);
        if($responseData->success):

             print_r("Working Fine"); exit;
        else:
             print_r("No valid Key"); exit;
        endif;
    } else {
        print_r("Not Working Captcha"); exit;
    }

?>
1
  • 35
    You should not put your secret key in a public SO question. If you haven't already, I'd recommend generating a new one now that you have shared this with the world.
    – sfarbota
    Aug 15, 2018 at 22:29

6 Answers 6

26

A simple example of a contact form verified by Google reCAPTCHA v3 with pure JavaScript and PHP

tldr; skip to code at the bottom.

Relevant reCAPTCHA docs etc:

(If Google are listening, we love your work and it would be wonderful to have some more elaborate examples linked to the above pages please.)

Overview:

  1. Get keys from Google
  2. Load recaptcha/api.js in head of html
  3. Hijack form submission with JavaScript and at that point get token from Google
  4. Submit form with token to your server
  5. Make request from your website's backend to Google to verify the form submission
  6. Interpret the response and proceed as necessary

Important to note: the 'success' response parameter indicates only whether or not the captcha was evaluated successfully, it doesn't indicate whether the submission was likely to be spam or not.

The 'score' parameter is the result you need to know about. The higher the score (a number between 0 and 1) the more likely a submission is genuine, and it's upto you what threshold (e.g. 0.5) to accept.

In detail:

Add the following line to the head of your HTML to load the recaptcha api.js code:

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

(where $reCAPTCHA_site_key is your public 'site key', which I've saved in a 'config.php' file.)

You need to submit a token (received from Google & unique to each form submission) to your server. I think it's simplest to send it via POST along with the rest of the form data. To that end I include a hidden field in the form as follows:

<form id="contactForm" method="post" action="contact">
    <!-- other form inputs -->
    <input type="hidden" id="gRecaptchaResponse" name="gRecaptchaResponse">
    <input type="submit" name="contact_submit" value="Send message">
</form>

(Nb. "contact" is contact.php, but I've 'rewritten' the url with .htaccess)

Now we need to hijack the default form submission to generate the token. We could generate the token on page load but since the token is only valid for two minutes (if I'm reading the https://developers.google.com/recaptcha/docs/verify page correctly) I think it's better to fetch it at the point of needing to send it to your site's server.

To this end I added the following right after the closing form tag:

<script>
    contactForm.addEventListener('submit', event => {
        event.preventDefault()
        validate(contactForm)
    });
</script>

I've put the validate(form) function just before the closing body tag:

function validate(form) {
    //perform optional error checking on form. If no errors then request a token and put it into the hidden field
    getRecaptchaToken(form)
}

//some other (optional) form validation functions

function getRecaptchaToken(form) {
    grecaptcha.ready(function() {
        grecaptcha.execute($reCAPTCHA_site_key, {action: 'contactForm'}).then(function(token) {
            gRecaptchaResponse.value = token //set the value of the hidden field
            form.submit() //submit the form
        });
    });
}

Notes:

  • $reCAPTCHA_site_key is your public Site Key
  • action: 'contactForm' identifies the submission of this particular form in the Google reCAPTCHA dashboard, and confirming it is as expected in the backend is a recommended extra security step

In the main PHP file, when the form submission is received:

//get the IP address of the origin of the submission
$ip = $_SERVER['REMOTE_ADDR'];

//construct the url to send your private Secret Key, token and (optionally) IP address of the form submitter to Google to get a spam rating for the submission (I've saved '$reCAPTCHA_secret_key' in config.php)
$url =  'https://www.google.com/recaptcha/api/siteverify?secret=' . urlencode($reCAPTCHA_secret_key) . '&response=' . urlencode($g_recaptcha_response) . '&remoteip=' . urlencode($ip);

//save the response, e.g. print_r($response) prints { "success": true, "challenge_ts": "2019-07-24T11:19:07Z", "hostname": "your-website-domain.co.uk", "score": 0.9, "action": "contactForm" }
$response = file_get_contents($url);

//decode the response, e.g. print_r($responseKeys) prints Array ( [success] => 1 [challenge_ts] => 2019-07-24T11:19:07Z [hostname] => your-website-domain.co.uk [score] => 0.9 [action] => contactForm )
$responseKeys = json_decode($response, true);

//check if the test was done OK, if the action name is correct and if the score is above your chosen threshold (again, I've saved '$g_recaptcha_allowable_score' in config.php)
if ($responseKeys["success"] && $responseKeys["action"] == 'contactForm') {
    if ($responseKeys["score"] >= $g_recaptcha_allowable_score) {
        //send email with contact form submission data to site owner/ submit to database/ etc
        //redirect to confirmation page or whatever you need to do
    } elseif ($responseKeys["score"] < $g_recaptcha_allowable_score) {
        //failed spam test. Offer the visitor the option to try again or use an alternative method of contact.
    }
} elseif($responseKeys["error-codes"]) { //optional
    //handle errors. See notes below for possible error codes
    //personally I'm probably going to handle errors in much the same way by sending myself a the error code for debugging and offering the visitor the option to try again or use an alternative method of contact
} else {
    //unkown screw up. Again, offer the visitor the option to try again or use an alternative method of contact.
}

Notes:

  • This is is the data which will be in the response from Google (returned as a JSON object):


   {
     "success": true|false,      // whether this request was a valid reCAPTCHA token for your site
     "score": number             // the score for this request (0.0 - 1.0)
     "action": string            // the action name for this request (important to verify)
     "challenge_ts": timestamp,  // timestamp of the challenge load (ISO format yyyy-MM-dd'T'HH:mm:ssZZ)
     "hostname": string,         // the hostname of the site where the reCAPTCHA was solved
     "error-codes": [...]        // optional
   }

  • These are the possible error codes:
    • missing-input-secret: The secret parameter is missing.
    • invalid-input-secret: The secret parameter is invalid or malformed.
    • missing-input-response: The response parameter is missing.
    • invalid-input-response: The response parameter is invalid or malformed.
    • bad-request: The request is invalid or malformed.
    • timeout-or-duplicate: The response is no longer valid; either is too old or has been used previously.

Putting it all together:

contact.php

<?php  //contact.php

    require_once('config.php');

    //do server-side validation of other form fields

    if (/*form has been submitted and has passed server-side validation of the other form fields*/) {
        $ip = $_SERVER['REMOTE_ADDR'];
        $url =  'https://www.google.com/recaptcha/api/siteverify?secret=' . urlencode($reCAPTCHA_secret_key) . '&response=' . urlencode($g_recaptcha_response) . '&remoteip=' . urlencode($ip);
        $response = file_get_contents($url);
        $responseKeys = json_decode($response, true);

        if ($responseKeys["success"] && $responseKeys["action"] == 'contactForm') {
            if ($responseKeys["score"] >= $g_recaptcha_allowable_score) {
                //send email with contact form submission data to site owner/ submit to database/ etc
                //redirect to confirmation page or whatever you need to do
            } elseif ($responseKeys["score"] < $g_recaptcha_allowable_score) {
                //failed spam test. Offer the visitor the option to try again or use an alternative method of contact.
            }
        } elseif($responseKeys["error-codes"]) { //optional
            //handle errors. See notes below for possible error codes
            //(I handle errors by sending myself an email with the error code for debugging and offering the visitor the option to try again or use an alternative method of contact)
        } else {
            //unkown screw up. Again, offer the visitor the option to try again or use an alternative method of contact.
        }

        exit;

    } else { //(re)display the page with the form

        echo <<<_END

            <!DOCTYPE html>
            <html lang="en">
                <head>
                    <title>Contact | Your website</title>
                    <link rel="stylesheet" href="css/style.css">
                    <script src="https://www.google.com/recaptcha/api.js?render=$reCAPTCHA_site_key"></script>
                </head>
                <body>

                    <!-- header etc -->

                    <form id="contactForm" method="post" action="contact">
                        //other form inputs
                        <input type="hidden" id="gRecaptchaResponse" name="gRecaptchaResponse">
                        <input type="submit" name="contact_submit" value="Send message">
                    </form>
                    <script>
                        contactForm.addEventListener('submit', event => {
                            event.preventDefault()
                            validate(contactForm)
                        });
                    </script>

                    <!-- footer etc -->

                    <script>
                        function validate(form) {
                            //perform optional client-side error checking of the form. If no errors are found then request a token and put it into the hidden field. Finally submit the form.
                            getRecaptchaToken(form)
                        }

                        //some (optional) form field validation functions

                        function getRecaptchaToken(form) {
                            grecaptcha.ready(function() {
                                grecaptcha.execute($reCAPTCHA_site_key, {action: 'contactForm'}).then(function(token) {
                                    gRecaptchaResponse.value = token
                                    form.submit()
                                });
                            });
                        }
                    </script>
                </body>
            </html>

_END;

config.php

<?php //config.php

//other site settings

// Google reCAPTCHA v3 keys
// For reducing spam contact form submissions

// Site key (public)
$reCAPTCHA_site_key = 'N0t-a-real-0N3_JHbnbUJ-BLAHBLAH_Blahblah';

// Secret key
$reCAPTCHA_secret_key = 'N0t-a-real-0N3_i77tyYGH7Ty6UfG-blah';

// Min score returned from reCAPTCHA to allow form submission
$g_recaptcha_allowable_score = 0.5; //Number between 0 and 1. You choose this. Setting a number closer to 0 will let through more spam, closer to 1 and you may start to block valid submissions.
6
  • grecaptcha.ready runs if the captcha-libry is loaded. So your code won't execute it
    – Adam
    Feb 4, 2020 at 22:58
  • @Adam It seemed to work ok for me. I had a look at your Q&A at stackoverflow.com/questions/60067216/… and the only difference I can see is that we've done things in a different order; I 'prevent default' outside of grecaptcha.ready and you do it inside-?
    – wkille
    Feb 5, 2020 at 10:47
  • "action": string // the action name for this request (important to verify) -- does anyone know why is it so important, how it possibly could be anything different than we sent and what it means and what to do about it when it's not the same?
    – Iorweth333
    Oct 19, 2022 at 9:30
  • developers.google.com/recaptcha/docs/v3#actions seems to suggest that including the action gives the recaptcha service a bit more context and in turn improves its ability to identify dodgy behaviour. But I don't think it's actually properly explained anywhere why or how.
    – wkille
    Oct 20, 2022 at 10:16
  • Any idea why I would get Attempt to read property "success" on null when it gets to line if($responseKeys["success"] ... )?
    – swinn
    Jul 14, 2023 at 2:16
17

Try this.

<script>
  grecaptcha.ready(function() {
   grecaptcha.execute('YOUR_SITE_KEY', {action: 'MyForm'})
   .then(function(token) {
    console.log(token)
    document.getElementById('g-recaptcha-response').value =    token;
   }); 
  }); 
 </script> 

<form action="verify.php" method="post">
  <input type="hidden" id="g-recaptcha-response" name="g-recaptcha-response">
  <input type="text" name="name" placeholder="Your name" required >
  <input type="email" name="email" placeholder="Your email address" required>
  <input type="submit" name="submit" value="SUBMIT" >
</form>
1
  • 4
    You mustn't have to show site secret in client side. It's a typo, use site key instead of site secret. (site secret's place in backend :))
    – Sentence
    Aug 5, 2019 at 9:25
16
<html>
    <head>
        <script src='https://www.google.com/recaptcha/api.js?render=6Le7-FkUAAAAADDSsTVBvpoUB5MkesNKgPVemFf-UD'></script>
    </head>
    <body> 
    <script>
    // when form is submit
    $('form').submit(function() { 
        // we stoped it
        event.preventDefault();
        // needs for recaptacha ready
        grecaptcha.ready(function() {
            // do request for recaptcha token
            // response is promise with passed token
            grecaptcha.execute('6Le7-FkUAAAAADDSsTVBvpoUB5MkesNKgPVemFf-UD', {action: 'create_comment'}).then(function(token) {
                // add token to form
                $('form').prepend('<input type="hidden" name="token" value="' + token + '">');
                $('form').prepend('<input type="hidden" name="action" value="create_comment">');
                // submit form now
                $('form').unbind('submit').submit();
            });;
        });
    });

    </script>

    <form action="verify.php" method="post">
        <input type="text" name="name" placeholder="Your name" required >
        <input type="email" name="email" placeholder="Your email address" required>
        <textarea name="message" placeholder="Type your message here...." required></textarea>   

        <input type="submit" name="submit" value="SUBMIT">

    </form>

    </body>

</html>

php

$token = $_POST['token'];
$secret = 'ur secret';
$action = $_POST['action'];
// now you need do a POST requst to google recaptcha server.
// url: https://www.google.com/recaptcha/api/siteverify.
// with data secret:$secret and response:$token 

At this point in the code, you will need to do a post request to ReCAPTCHA to verify the token, as documented here: https://www.google.com/recaptcha/api/siteverify. The response will be a json object with field "success" (true/false) and "action" for comparison (==) and score (number from 0.0 - 1.0)

https://developers.google.com/recaptcha/docs/v3#api-response.

You can also specify action name for each request (create_post, update_post, create_comment ...)

5
  • 3
    $token = $_POST['token']; $action = $_POST['action']; if(isset($token) && !empty($token)){ //get verify response data $verifyResponse = file_get_contents('google.com/recaptcha/api/…); $responseData = json_decode($verifyResponse); } if($responseData->success) { //Captcha is good }
    – Claudio
    Jul 14, 2018 at 19:13
  • 1
    this doesn't seem to work on V3 and seems to be a mix of v2 and v3 methods.
    – Dave
    Sep 3, 2018 at 15:07
  • 1
    @Dave if you know of a better method to implement v3 please provide an answer
    – BKlassen
    Nov 8, 2018 at 16:57
  • This worked for me on v3, except instead of using prepend (which didn't work properly under certain circumstances) I've simply added a hidden input and then in the .then() function I set the val() of the input. Dec 24, 2018 at 5:58
  • 1
    Find a working example of the php code here: pastebin.com/ueNaK41q made some improvements to the one @Claudio provided.
    – maysi
    Jan 6, 2019 at 22:30
3

I'd like to give you a complete workflow to integrate recaptchav3 into an ASP.NET core MVC solution.

in your appsettings.json file:

  "RecaptchaSettings": {
    "Uri": "https://www.google.com/recaptcha/api/siteverify",
    "SecretKey": "your private key"
    "SiteKey": "your public key",
    "Version": "v3"
  }

in your view (@razor syntax):

@using Microsoft.Extensions.Configuration
@inject IConfiguration Configuration

<script src="https://www.google.com/recaptcha/[email protected]("RecaptchaSettings")["SiteKey"]"></script>

    <script>
        grecaptcha.ready(function () {
            grecaptcha.execute('@Configuration.GetSection("RecaptchaSettings")["SiteKey"]',  { action: 'homepage' })
                .then(function (token) {

                    document.getElementById('g-recaptcha-response').value = token;
                });
        });
    </script>

and in your form put this:

<form action="/">
…
<input type="hidden" id="g-recaptcha-response" name="g-recaptcha-response">
…

</form>

I create a simple method to manage it:

public async Task<bool> ChallengePassed(string uri, string gRecaptchaResponse, string secret)
        {

            var concUri = uri + "?secret=" + secret + "&response=" + gRecaptchaResponse;

            var request = new HttpRequestMessage(HttpMethod.Get, concUri);
            var res = await _Client.SendAsync(request);

            if (!res.IsSuccessStatusCode)
            {
                return false;
            }

            var data = await res.Content.ReadAsStringAsync();

            dynamic JSONdata = JObject.Parse(data);
            if (JSONdata.success != "true")
            {
                return false;
            }

            return true;
        }

        #endregion

        #region PRIVATE

        #endregion

        #endregion

        #endregion
    }

and simply I called it into a Controller:

 //recaptcha validation

    bool isChallengeOk = await _CaptchaVerify.ChallengePassed(_Configuration.GetValue<string>("RecaptchaSettings:Uri"), Request.Form["g-recaptcha-response"], _Configuration.GetValue<string>("RecaptchaSettings:SecretKey"));

notice that I'm setting the input parameters from the "_Configuration" object, that represents an instance of configuration setting object in Startup.cs. You can pass manually input parameters to the method.

Enjoy it

1
  • Thanks for that. Google doc says it must be a post request and the parameters must be sent as payload, also the response doesn't contain the score but your example works as needed
    – VladL
    Aug 10, 2023 at 19:59
2

Here is a sample working code with the demo.

html side code

<html>
  <head>
    <title>Google recapcha v3 demo - Codeforgeek</title>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
      <script src="https://www.google.com/recaptcha/api.js?render=put your site key here"></script>
  </head>
  <body>
    <h1>Google reCAPTHA Demo</h1>
    <form id="comment_form" action="form.php" method="post" >
      <input type="email" name="email" placeholder="Type your email" size="40"><br><br>
      <textarea name="comment" rows="8" cols="39"></textarea><br><br>
      <input type="submit" name="submit" value="Post comment"><br><br>
    </form>
      <script>
       // when form is submit
    $('#comment_form').submit(function() {
        // we stoped it
        event.preventDefault();
        var email = $('#email').val();
        var comment = $("#comment").val();
        // needs for recaptacha ready
        grecaptcha.ready(function() {
            // do request for recaptcha token
            // response is promise with passed token
            grecaptcha.execute('put your site key here', {action: 'create_comment'}).then(function(token) {
                // add token to form
                $('#comment_form').prepend('<input type="hidden" name="g-recaptcha-response" value="' + token + '">');
                    $.post("form.php",{email: email, comment: comment, token: token}, function(result) {
                            console.log(result);
                            if(result.success) {
                                    alert('Thanks for posting comment.')
                            } else {
                                    alert('You are spammer ! Get the @$%K out.')
                            }
                    });
            });;
        });
  });
  </script>
  </body>
</html>

PHP code.

<?php

        $email;$comment;$captcha;
        if(isset($_POST['email'])){
          $email=$_POST['email'];
        }if(isset($_POST['comment'])){
          $comment=$_POST['comment'];
        }if(isset($_POST['token'])){
          $captcha=$_POST['token'];
          }
        if(!$captcha){
          echo '<h2>Please check the the captcha form.</h2>';
          exit;
        }
        $secretKey = "put your secret key here";
        $ip = $_SERVER['REMOTE_ADDR'];

        // post request to server

        $url =  'https://www.google.com/recaptcha/api/siteverify?secret=' . urlencode($secretKey) .  '&response=' . urlencode($captcha);
        $response = file_get_contents($url);
        $responseKeys = json_decode($response,true);
        header('Content-type: application/json');
        if($responseKeys["success"]) {
                echo json_encode(array('success' => 'true'));
        } else {
                echo json_encode(array('success' => 'false'));
        }
?>

Its working fine.

Demo: https://demo.codeforgeek.com/recaptcha-v3/

tutorial: https://codeforgeek.com/2019/02/google-recaptcha-v3-tutorial/

0

Here is the sample php script to verify recaptcha, it works for both v2 and v3.

<?php
function reCaptcha($recaptcha){
    $secret = "6LenFiQlAAAAANQmiVnxv6MtLmDFSiDeM4CWBjnG";
    $ip = $_SERVER['REMOTE_ADDR'];

    $postvars = array("secret"=>$secret, "response"=>$recaptcha, "remoteip"=>$ip);
    $url = "https://www.google.com/recaptcha/api/siteverify";

    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_TIMEOUT, 10);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $postvars);
    $data = curl_exec($ch);
    curl_close($ch);

    return json_decode($data, true);
}
$recaptcha = $_POST['g-recaptcha-response'];

$res = reCaptcha($recaptcha);

if($res['success']){
    $email = $_POST['email'];
    echo "Success ".$email;
}
else{
    echo "CAPTCHA Failed";
}
?>

You need to send a post request with secretkey, ip and the captcha reseponse.

If you need full tutorial on how to integrate recapatcha v3 with sample html and php code, read this tutorial https://geekvortex.com/integrate-invisible-recaptcha-v3-html-php

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.