77

I've just set up the new google recaptcha with checkbox, it's working fine on front end, however I don't know how to handle it on server side using PHP. I've tried to use the old code below but the form is sent even if the captcha is not valid.

require_once('recaptchalib.php');
$privatekey = "my key";
$resp = recaptcha_check_answer ($privatekey,
        $_SERVER["REMOTE_ADDR"],
        $_POST["recaptcha_challenge_field"],
        $_POST["recaptcha_response_field"]);

if (!$resp->is_valid) {
 $errCapt='<p style="color:#D6012C ">The CAPTCHA Code wasnot entered correctly.</p>';}
loler
  • 2,594
  • 1
  • 20
  • 30
Moatez
  • 771
  • 1
  • 6
  • 4
  • Check it's been filled in. `if( strlen($_POST['recaptcha_challenge_field']) > 0) {}` – ʰᵈˑ Dec 03 '14 at 14:30
  • thank you for response but can you give me more details please... – Moatez Dec 03 '14 at 15:06
  • 1
    I just implemented this myself on a contact form. I'd suggest using the new Google-supplied `recaptchalib.php` along with their example at this link: https://github.com/google/ReCAPTCHA/tree/master/php – Ed Stenger Dec 03 '14 at 15:18
  • thank you, but i am sorry i didn't understand yet, i have two files contact.html and contact.php, the first call the second, in thé first i put the following : before : before the submit button
    but in contact.php i dont know what i should put ??
    – Moatez Dec 05 '14 at 00:25

12 Answers12

129

Private key safety

While the answers here are definately working, they are using a GET request, which exposes your private key (even though https is used). On Google Developers the specified method is POST.

For a little bit more detail: https://stackoverflow.com/a/323286/1680919

Verification via POST

function isValid() 
{
    try {

        $url = 'https://www.google.com/recaptcha/api/siteverify';
        $data = ['secret'   => '[YOUR SECRET KEY]',
                 'response' => $_POST['g-recaptcha-response'],
                 'remoteip' => $_SERVER['REMOTE_ADDR']];
                 
        $options = [
            'http' => [
                'header'  => "Content-type: application/x-www-form-urlencoded\r\n",
                'method'  => 'POST',
                'content' => http_build_query($data) 
            ]
        ];
    
        $context  = stream_context_create($options);
        $result = file_get_contents($url, false, $context);
        return json_decode($result)->success;
    }
    catch (Exception $e) {
        return null;
    }
}

Array Syntax: I use the "new" array syntax ( [ and ] instead of array(..) ). If your php version does not support this yet, you will have to edit those 3 array definitions accordingly (see comment).

Return Values: This function returns true if the user is valid, false if not, and null if an error occured. You can use it for example simply by writing if (isValid()) { ... }

Community
  • 1
  • 1
Levite
  • 17,263
  • 8
  • 50
  • 50
  • 2
    If you need the *old array syntax*, change the `$data = ...` and `$options = ...` declarations to this: `$data = array('secret' => '[YOUR SECRET KEY]', 'response' => $_POST['g-recaptcha-response'], 'remoteip' => $_SERVER['REMOTE_ADDR']); $options = array( 'http' => array( 'header' => "Content-type: application/x-www-form-urlencoded\r\n", 'method' => 'POST', 'content' => http_build_query($data) ) );` – Levite Jun 10 '15 at 07:06
  • 4
    You can save a request to the server and bail ahead of time: ```if (empty($_POST['g-recaptcha-response'])) { return false; }``` – Westy92 Nov 28 '16 at 03:47
  • 6
    https urls are actually encrypted, so the private key is not exposed even when using a GET request, see: http://stackoverflow.com/questions/499591/are-https-urls-encrypted – Varga Tamas Dec 08 '16 at 14:06
  • 1
    Please note, for debugging purposes, you can only call `isValid()` once. If called a second time, it will return false. Found this out the hard way with an echo statement... – Sablefoste May 12 '17 at 16:58
  • 1
    None of the function used throw exceptions so the try{}catch() is pointless – chris Feb 02 '18 at 10:59
  • Even though https is encrypted, the private key could still be exposed in logs or other mechanisms. Of course, it's extremely unlikely Google's logs will be compromised, but it's always good to be a little paranoid when there's no/negligible expense. – Brian C Mar 09 '19 at 05:38
  • 1
    @chris: When the decoded $result does not have the member `success` (e.g. because it is null), this should apply (problems with network connections, blocked google (firewall policies), etc. come to mind), possibly rare, but reasonable to handle I would argue). – Levite Mar 11 '19 at 13:12
  • @BrianC: Definitely. Well it is actually discouraged sending sensitive data via GET in general (even when using https), I added a good (imho) SO answer about the topic. – Levite Mar 11 '19 at 13:22
  • @VargaTamas Yes, but also don't forget reuse of code and best-practice (also for other use cases), even though it might not (directly visually) be bad in this situation/use case. E.g. in the client-side browser history such (obviously) will show up in plain text. While it might not be that tragic here, it is not guaranteed that your users are always using incognito mode, lock the PC, or not at all share the login/workstation with others. – Levite Nov 17 '22 at 12:16
115

this is solution

index.html

<html>
  <head>
    <title>Google recapcha demo - Codeforgeek</title>
    <script src='https://www.google.com/recaptcha/api.js'></script>
  </head>
  <body>
    <h1>Google reCAPTHA Demo</h1>
    <form id="comment_form" action="form.php" method="post">
      <input type="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>
      <div class="g-recaptcha" data-sitekey="=== Your site key ==="></div>
    </form>
  </body>
</html>

verify.php

<?php
    $email; $comment; $captcha;

    if(isset($_POST['email']))
        $email=$_POST['email'];
    if(isset($_POST['comment']))
        $comment=$_POST['comment'];
    if(isset($_POST['g-recaptcha-response']))
        $captcha=$_POST['g-recaptcha-response'];

    if(!$captcha){
        echo '<h2>Please check the the captcha form.</h2>';
        exit;
    }

    $response = json_decode(file_get_contents("https://www.google.com/recaptcha/api/siteverify?secret=YOUR SECRET KEY&response=".$captcha."&remoteip=".$_SERVER['REMOTE_ADDR']), true);
    if($response['success'] == false)
    {
        echo '<h2>You are spammer ! Get the @$%K out</h2>';
    }
    else
    {
        echo '<h2>Thanks for posting comment.</h2>';
    }
?>

http://codeforgeek.com/2014/12/google-recaptcha-tutorial/

CarenRose
  • 1,266
  • 1
  • 12
  • 24
SoCix
  • 1,396
  • 1
  • 10
  • 7
  • 15
    The reCAPTCHA form field value is being concatenated directly into the verify URL. An attacker could use that to inject arbitrary form parameters on the verify request, potentially circumventing the CAPTCHA. To solve this problem, sanitize the captcha variable like this: `urlencode($captcha)` – sffc Oct 01 '15 at 08:19
  • 2
    this code does not encode the parameters which can be used by attackers to break your code. – Paul Mar 25 '16 at 07:53
  • 6
    The data should be POSTed as specified [in the docs](https://developers.google.com/recaptcha/docs/verify#api-request). This code uses a GET method. I'd go with a variation on [Levit's answer](http://stackoverflow.com/a/30749288/412004). – ͢bts Jun 23 '16 at 11:59
  • 2
    Worked well for me. Except (in index.html) on the form line: `action="form.php"`, it should be `action="verify.php"`. – Joe Apr 14 '18 at 21:50
  • This codes works on my Shared hosting & not on VPS server. Are there any requirements linked to server? – Mohammed Kumel Mar 27 '19 at 00:41
  • gotta love that response from server – pttsky Apr 26 '21 at 17:13
27

I'm not a fan of any of these solutions. I use this instead:

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://www.google.com/recaptcha/api/siteverify");
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, [
    'secret' => $privatekey,
    'response' => $_POST['g-recaptcha-response'],
    'remoteip' => $_SERVER['REMOTE_ADDR']
]);

$resp = json_decode(curl_exec($ch));
curl_close($ch);

if ($resp->success) {
    // Success
} else {
    // failure
}

I'd argue that this is superior because you ensure it is being POSTed to the server and it's not making an awkward 'file_get_contents' call. This is compatible with recaptcha 2.0 described here: https://developers.google.com/recaptcha/docs/verify

I find this cleaner. I see most solutions are file_get_contents, when I feel curl would suffice.

mattgen88
  • 281
  • 3
  • 2
8

Easy and best solution is the following.
index.html

<form action="submit.php" method="POST">
   <input type="text" name="name" value="" />
   <input type="text" name="email" value="" />
   <textarea type="text" name="message"></textarea>
   <div class="g-recaptcha" data-sitekey="Insert Your Site Key"></div>
   <input type="submit" name="submit" value="SUBMIT">
</form>

submit.php

<?php
if(isset($_POST['submit']) && !empty($_POST['submit'])){
  if(isset($_POST['g-recaptcha-response']) && !empty($_POST['g-recaptcha-response'])){
    //your site secret key
    $secret = 'InsertSiteSecretKey';
    //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){
        //contact form submission code goes here

        $succMsg = 'Your contact request have submitted successfully.';
    }else{
        $errMsg = 'Robot verification failed, please try again.';
    }
  }else{
    $errMsg = 'Please click on the reCAPTCHA box.';
  }
}
?>

I have found this reference and full tutorial from here - Using new Google reCAPTCHA with PHP

JoyGuru
  • 1,803
  • 20
  • 11
7

I liked Levit's answer and ended up using it. But I just wanted to point out, just in case, that there is an official Google PHP library for new reCAPTCHA: https://github.com/google/recaptcha

The latest version (right now 1.1.2) supports Composer and contains an example that you can run to see if you have configured everything correctly.

Below you can see part of the example that comes with this official library (with my minor modifications for clarity):

// Make the call to verify the response and also pass the user's IP address
    $resp = $recaptcha->verify($_POST['g-recaptcha-response'], $_SERVER['REMOTE_ADDR']);

    if ($resp->isSuccess()) {
// If the response is a success, that's it!
        ?>
        <h2>Success!</h2>
        <p>That's it. Everything is working. Go integrate this into your real project.</p>
        <p><a href="/">Try again</a></p>
        <?php
    } else {
// If it's not successful, then one or more error codes will be returned.
        ?>
        <h2>Something went wrong</h2>
        <p>The following error was returned: <?php
            foreach ($resp->getErrorCodes() as $code) {
                echo '<tt>' , $code , '</tt> ';
            }
            ?></p>
        <p>Check the error code reference at <tt><a href="https://developers.google.com/recaptcha/docs/verify#error-code-reference">https://developers.google.com/recaptcha/docs/verify#error-code-reference</a></tt>.
        <p><strong>Note:</strong> Error code <tt>missing-input-response</tt> may mean the user just didn't complete the reCAPTCHA.</p>
        <p><a href="/">Try again</a></p>
    <?php
    }

Hope it helps someone.

Yerke
  • 2,071
  • 2
  • 25
  • 23
3

To verify at server side using PHP. Two most important thing you need to consider.

1. $_POST['g-recaptcha-response']

2.$secretKey = '6LeycSQTAAAAAMM3AeG62pBslQZwBTwCbzeKt06V';

$verifydata = file_get_contents('https://www.google.com/recaptcha/api/siteverify?secret='.$secretKey.'&response='.$_POST['g-recaptcha-response']);
        $response= json_decode($verifydata);

If you get $verifydata true, You done.
For more check out this
Google reCaptcha Using PHP | Only 2 Step Integration

Roshan Padole
  • 390
  • 4
  • 11
2

In the example above. For me, this if($response.success==false) thing does not work. Here is correct PHP code:

$url = 'https://www.google.com/recaptcha/api/siteverify';
$privatekey = "--your_key--";
$response = file_get_contents($url."?secret=".$privatekey."&response=".$_POST['g-recaptcha-response']."&remoteip=".$_SERVER['REMOTE_ADDR']);
$data = json_decode($response);

if (isset($data->success) AND $data->success==true) {
            // everything is ok!
} else {
            // spam
}
  • 2
    This is because you're not passing `true` as the 2nd parameter to `json_decode`. By default, `json_decode` returns an object (jsOn), but passing `true` will allow it to return an array. – Curtis Mattoon Apr 23 '15 at 13:11
  • "In the example above..." please note that the StackExchange platform sorts answers according to their ranking by votes. The example above is clearly not the one you were referring to. – Nicolas Jul 18 '18 at 03:41
1

it is similar with mattgen88, but I just fixed CURLOPT_HEADER, and redefine array for it work in domain.com host server. this one doesn't work on my xampp localhost. Those small error but took long to figure out. this code was tested on domain.com hosting.

   $privatekey = 'your google captcha private key';

    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, "https://www.google.com/recaptcha/api/siteverify");
    curl_setopt($ch, CURLOPT_HEADER, 'Content-Type: application/json');
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 
    curl_setopt($ch, CURLOPT_POST, 1);
    curl_setopt($ch, CURLOPT_POSTFIELDS, array(
        'secret' => $privatekey,
        'response' => $_POST['g-recaptcha-response'],
        'remoteip' => $_SERVER['REMOTE_ADDR']
    )
               );

    $resp = json_decode(curl_exec($ch));
    curl_close($ch);

    if ($resp->success) {
        // Success
        echo 'captcha';
    } else {
        // failure
        echo 'no captcha';
    }
Võ Minh
  • 155
  • 2
  • 12
1

Here you have simple example. Just remember to provide secretKey and siteKey from google api.

<?php
$siteKey = 'Provide element from google';
$secretKey = 'Provide element from google';

if($_POST['submit']){
    $username = $_POST['username'];
    $responseKey = $_POST['g-recaptcha-response'];
    $userIP = $_SERVER['REMOTE_ADDR'];
    $url = "https://www.google.com/recaptcha/api/siteverify?secret=$secretKey&response=$responseKey&remoteip=$userIP";
    $response = file_get_contents($url);
    $response = json_decode($response);
    if($response->success){
        echo "Verification is correct. Your name is $username";
    } else {
        echo "Verification failed";
    }
} ?>

<html>
<meta>
    <title>Google ReCaptcha</title>
</meta>
<body>
    <form action="index.php" method="post">
        <input type="text" name="username" placeholder="Write your name"/>
        <div class="g-recaptcha" data-sitekey="<?= $siteKey ?>"></div>
        <input type="submit" name="submit" value="send"/>
    </form>
    <script src='https://www.google.com/recaptcha/api.js'></script>
</body>

Adam Kozlowski
  • 5,606
  • 2
  • 32
  • 51
1

Source Tutorial Link

V2 of Google reCAPTCHA.

Step 1 - Go to Google reCAPTCHA

Login then get Site Key and Secret Key

Step 2 - Download PHP code here and upload src folder on your server.

Step 3 - Use below code in your form.php

        <head>
            <title>FreakyJolly.com Google reCAPTCHA EXAMPLE form</title>
            <script src='https://www.google.com/recaptcha/api.js'></script>
        </head>

        <body>
            <?php

            require('src/autoload.php');

            $siteKey = '6LegPmIUAAAAADLwDmXXXXXXXyZAJVJXXXjN';
            $secret = '6LegPmIUAAAAAO3ZTXXXXXXXXJwQ66ngJ7AlP';

            $recaptcha = new \ReCaptcha\ReCaptcha($secret);

            $gRecaptchaResponse = $_POST['g-recaptcha-response']; //google captcha post data
            $remoteIp = $_SERVER['REMOTE_ADDR']; //to get user's ip

            $recaptchaErrors = ''; // blank varible to store error

            $resp = $recaptcha->verify($gRecaptchaResponse, $remoteIp); //method to verify captcha
            if ($resp->isSuccess()) {

               /******** 

                Add code to create User here when form submission is successful

               *****/

            } else {

                /****

                // This variable will have error when reCAPTCHA is not entered correctly.

                ****/

               $recaptchaErrors = $resp->getErrorCodes(); 
            }


            ?>
                <form autcomplete="off" class="form-createuser" name="create_user_form" action="" method="post">
                    <div class="panel periodic-login">
                        <div class="panel-body text-center">
                            <div class="form-group form-animate-text" style="margin-top:40px !important;">
                                <input type="text" autcomplete="off" class="form-text" name="new_user_name" required="">
                                <span class="bar"></span>
                                <label>Username</label>
                            </div>
                            <div class="form-group form-animate-text" style="margin-top:40px !important;">
                                <input type="text" autcomplete="off" class="form-text" name="new_phone_number" required="">
                                <span class="bar"></span>
                                <label>Phone</label>
                            </div>
                            <div class="form-group form-animate-text" style="margin-top:40px !important;">
                                <input type="password" autcomplete="off" class="form-text" name="new_user_password" required="">
                                <span class="bar"></span>
                                <label>Password</label>
                            </div>
                            <?php 
                                    if(isset($recaptchaErrors[0])){
                                        print('Error in Submitting Form. Please Enter reCAPTCHA AGAIN');
                                    }
                              ?>
                            <div class="g-recaptcha" data-sitekey="6LegPmIUAAAAADLwDmmVmXXXXXXXXXXXXXXjN"></div>
                            <input type="submit" class="btn col-md-12" value="Create User">
                        </div>
                    </div>
                </form>
        </body>

        </html>
Code Spy
  • 9,626
  • 4
  • 66
  • 46
0

In response to @mattgen88's answer ,Here is a CURL method with better arrangement:

   //$secret= 'your google captcha private key';
    $curl = curl_init();
    curl_setopt_array($curl, array(
        CURLOPT_URL             => "https://www.google.com/recaptcha/api/siteverify",
        CURLOPT_HEADER          => "Content-Type: application/json",
        CURLOPT_RETURNTRANSFER  => true,
        CURLOPT_SSL_VERIFYPEER  => FALSE, // to disable ssl verifiction set to false else true
        //CURLOPT_ENCODING      => "",
        CURLOPT_MAXREDIRS       => 10,
        CURLOPT_TIMEOUT         => 30,
        CURLOPT_HTTP_VERSION    => CURL_HTTP_VERSION_1_1,
        CURLOPT_CUSTOMREQUEST   => "POST",
        CURLOPT_POSTFIELDS      => array(
            'secret' => $secret,
            'response' => $_POST['g-recaptcha-response'],
            'remoteip' => $_SERVER['REMOTE_ADDR']
        )
    ));

    $response = json_decode(curl_exec($curl));
    $err = curl_error($curl);

    curl_close($curl);

    if ($response->success) {
        echo 'captcha';
    } 
    else if ($err){
        echo $err;
    }   
    else {
        echo 'no captcha';
    }
MR_AMDEV
  • 1,712
  • 2
  • 21
  • 38
0

Check below example

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

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;
    }
    else
    {
         document.getElementById('captcha').innerHTML="Captcha completed";
        return true; 
    }
}

</script>
<form autocomplete="off" method="post" action=submit.php">

    <input type="text" name="name">
    <input type="text" name="email">
    <div class="g-recaptcha" id="rcaptcha"  data-sitekey="site key"></div>
    <span id="captcha" style="color:red" /></span> <!-- this will show captcha errors -->
    <input type="submit" id="sbtBrn" value="Submit" name="sbt" class="btn btn-info contactBtn" />
</form>
Pankaj Upadhyay
  • 2,114
  • 21
  • 22