How to validate Google reCAPTCHA v3 on server side?
Solution 1
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/
Solution 2
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()) { ... }
Solution 3
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.
Solution 4
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
Solution 5
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.
Moatez
Updated on July 05, 2022Comments
-
Moatez almost 2 years
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>';}
-
Curtis Mattoon about 9 yearsThis is because you're not passing
true
as the 2nd parameter tojson_decode
. By default,json_decode
returns an object (jsOn), but passingtrue
will allow it to return an array. -
Levite about 9 yearsIf 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) ) );
-
sffc over 8 yearsThe 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)
-
Paul over 8 yearsthis code does not encode the parameters which can be used by attackers to break your code.
-
͢bts about 8 yearsThe data should be POSTed as specified in the docs. This code uses a GET method. I'd go with a variation on Levit's answer.
-
Westy92 over 7 yearsYou can save a request to the server and bail ahead of time:
if (empty($_POST['g-recaptcha-response'])) { return false; }
-
Varga Tamas over 7 yearshttps urls are actually encrypted, so the private key is not exposed even when using a GET request, see: stackoverflow.com/questions/499591/are-https-urls-encrypted
-
Gregory R. over 7 yearsThis is a good working solution if you are unable to use the file_get_contents() due to 'allow_url_fopen' being disabled in php.ini. Works for me. +1.
-
Sablefoste about 7 yearsPlease 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... -
chris over 6 yearsNone of the function used throw exceptions so the try{}catch() is pointless
-
Joe about 6 yearsWorked well for me. Except (in index.html) on the form line:
action="form.php"
, it should beaction="verify.php"
. -
Nicolas almost 6 years"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.
-
Brian C over 5 yearsEven 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.
-
Levite over 5 years@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 over 5 years@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.
-
Mohammed Kumel over 5 yearsThis codes works on my Shared hosting & not on VPS server. Are there any requirements linked to server?
-
John Contarino over 5 yearsStraightforward and simple, and uses POST as required by Google
-
pttsky about 3 yearsgotta love that response from server