This page explains how to verify a user’s response to a reCAPTCHA challenge from your application’s
backend.
For web users, you can get the user’s response token in one of three ways:
g-recaptcha-response
POST parameter when the user submits the form on your sitegrecaptcha.getResponse(opt_widget_id)
after the user completes
the reCAPTCHA challenge- As a string argument to your callback function
ifdata-callback
is specified in either theg-recaptcha
tag attribute or
the callback parameter in thegrecaptcha.render
method
For Android library users, you can call the
SafetyNetApi.RecaptchaTokenResult.getTokenResult()
method to get response token if the status returns successful.
Token Restrictions
Each reCAPTCHA user response token is valid for two minutes, and can only be verified once to
prevent replay attacks. If you need a new token, you can re-run the reCAPTCHA verification.
After you get the response token, you need to verify it within two minutes with reCAPTCHA using the
following API to ensure the token is valid.
API Request
URL: https://www.google.com/recaptcha/api/siteverify
METHOD: POST
POST Parameter | Description |
---|---|
secret | Required. The shared key between your site and reCAPTCHA. |
response | Required. The user response token provided by the reCAPTCHA client-side integration on your site. |
remoteip | Optional. The user’s IP address. |
API Response
The response is a JSON object:
{
"success": true|false,
"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
}
For reCAPTCHA Android:
{
"success": true|false,
"challenge_ts": timestamp, // timestamp of the challenge load (ISO format yyyy-MM-dd'T'HH:mm:ssZZ)
"apk_package_name": string, // the package name of the app where the reCAPTCHA was solved
"error-codes": [...] // optional
}
Error code reference
Error code | Description |
---|---|
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. |
I got a contact form on my website on Laravel and I’d like to place a ReCaptcha v3 but for now the result I got from the verification is the error «timeout-or-duplicate».
Can you help me from A to Z ? I don’t know where to go…
My head :
<script src="https://www.google.com/recaptcha/api.js?render=My_Site_Key"></script>
<script>
grecaptcha.ready(function () {
grecaptcha.execute('My_Site_Key', { action: 'contact' }).then(function (token) {
var recaptchaResponse = document.getElementById('recaptchaResponse');
recaptchaResponse.value = token;
});
});
</script>
The contact form :
<form action="{{ route('contact.post') }}" id="contact-form" method="post" name="contactForm">
<input type="hidden" name="_token" id="token" value="{{ csrf_token() }}">
<input type="hidden" name="recaptcha_response" id="recaptchaResponse">
<fieldset>
<div class="col-sm-12">
<input id="name" name="name" placeholder="Nom*" type="text">
</div>
<div class="col-sm-12">
<input id="email" name="email" placeholder="Email*" type="text">
</div>
<div class="col-sm-12">
<input id="object" name="object" placeholder="Objet*" type="text" autocomplete="off">
</div>
<div class="col-xs-12">
<textarea cols="5" id="message" name="message" placeholder="Votre message...*"></textarea>
</div>
<div class="col-xs-12">
<button class="submit active" id="contact-submit">ENVOYER</button>
</div>
<div class="error col-xs-12">
<h3></h3>
</div>
<div class="success col-xs-12">
<h3>Merci ! Votre message a été envoyé !</h3>
</div>
</fieldset>
</form>
Route:
Route::post('/contact', array('as' => 'contact.post', 'uses' => 'ContactController@send'));
The Contact Controller :
<?php
namespace AppHttpControllers;
use IlluminateHttpRequest;
use AppHttpRequests;
use IlluminateSupportFacadesInput;
use IlluminateSupportFacadesMail;
class ContactController extends Controller
{
public function send() {
$info = array(
'name' => Input::get('name'),
'email' => Input::get('email'),
'object' => Input::get('object'),
'message' => Input::get('message')
);
if($info['name'] == "" || $info['email'] == "" || $info['object'] == "" || $info['message'] == "") {
return json_encode(['response' => 'Tous les champs doivent être remplis !']);
}
if(!filter_var($info['email'], FILTER_VALIDATE_EMAIL)) {
return json_encode(['response' => 'Vous devez rentrer une adresse e-mail valide !']);
}
$ip = Request()->ip();
// Build POST request:
$recaptcha_url = 'https://www.google.com/recaptcha/api/siteverify';
$recaptcha_secret = 'My_Secret_Key';
$recaptcha_response = $_POST['recaptcha_response'];
// Make and decode POST request:
$recaptcha = file_get_contents($recaptcha_url . '?secret=' . $recaptcha_secret . '&response=' . $recaptcha_response);
$recaptcha = json_decode($recaptcha);
// Take action based on the score returned:
if ($recaptcha->score < 0.5) {
return json_encode(['response' => 'Vous êtes considéré comme Bot/Spammer !', 'score' => $recaptcha->score]);
}
Mail::send(['email.html.contact', 'email.text.contact'], ['info' => $info, 'ip' => $ip], function($message) use ($info) {
$message->to('contact@bryangossuin.be')->subject('Bryan Gossuin | Formulaire de contact');
$message->replyTo($info['email'], $info['name']);
});
return json_encode(['response' => 'success','']);
}
}
Finaly the javascript
$('#contact-form').on('submit', function(e) {
e.preventDefault();
swal({
title: "Souhaitez-vous vraiment envoyer ce mail ?",
icon: "warning",
buttons: {
cancel: {
text: "Annuler",
value: false,
visible: true,
closeModal: true,
},
confirm: "Envoyer",
}
})
.then((value) => {
if (value) {
$.ajax({
method: "POST",
url: "contact",
cache: false,
data: $(this).serialize(),
dataType: 'json',
success: function(json) {
console.log(json.score);
if (json.response == 'success') {
$('#contact-form').trigger("reset");
swal("E-mail envoyé", "Merci de votre demande !", "success");
} else {
swal("Erreur !", json.response, "error");
}
}
}
)
}
});
});
The output I got from google is
{
"success": false,
"error-codes": [
"timeout-or-duplicate"
]
}
and I expect it to be
{
"success": true,
"score" : x,
"error-codes": '',
}
I guess the problem is because the « method post » is used two times because when I Check directly
On the API Google to verify the user token it show le thé code but right after I refresh the page it show me « timeout or duplicate » but I dont know how to fix this
<script>
grecaptcha.ready(function () {
grecaptcha.execute('GOOGLE CLIENT KEY', {action: 'contact'}).then(function(token) {
$('#email_form').prepend('<input id="recaptcha_token" type="hidden" name="token" value="' + token + '">');
console.log("recaptcha_token set on email_form >> token = " + token);
});
setInterval(function () {
grecaptcha.execute('GOOGLE CLIENT KEY', { action: 'contact' }).then(function (token) {
if ($("#recaptcha_token").length) {
console.log("set new token to existing input >> token = " + token);
$("#recaptcha_token").val(token);
} else {
console.error("recaptcha_token does not exist on email_form");
}
});
}, 60000);
});
</script>
I set the interval to one minute because I have no idea how long it takes for the timeout-or-duplicate
error to occur.
Answer by Natasha Pena
Token has been used previously. To confirm that, log the token value before is used (error log, local file, whatever),Validity time of the token expired (After you get the response token, you need to verify it within two minutes),Thus, if you spend more then 2 minutes on the contact-form, you get the timout error,. Thats why its recommended in the docs to only call execute if the user actually submits your form / takes action. In vanilla JS it would look like this:,I guess the problem is because the « method post » is used two times because when I Check directly
On the API Google to verify the user token it show le thé code but right after I refresh the page it show me « timeout or duplicate » but I dont know how to fix this
My resolution for 1, set an interval that calls the set token function, so it is refreshed every 2 minutes.
$(document).ready(function() {
SetCaptchaToken();
setInterval(function () { SetCaptchaToken(); }, 2 * 60 * 1000);
});
Answer by Declan Barrera
I set the interval to one minute because I have no idea how long it takes for the timeout-or-duplicate error to occur.,When using V3, the docs suggest that you can perform a grecaptcha.execute on page load.,I call this method just before send the form to the server, but ocassionally still have the «timeout-or-duplicate» error.«,I am finding though, that sporadically, some site verify responses are returning timeout-or-duplicate when I do a server-side site verification.
<script>
grecaptcha.ready(function () {
grecaptcha.execute('GOOGLE CLIENT KEY', {action: 'contact'}).then(function(token) {
$('#email_form').prepend('<input id="recaptcha_token" type="hidden" name="token" value="' + token + '">');
console.log("recaptcha_token set on email_form >> token = " + token);
});
setInterval(function () {
grecaptcha.execute('GOOGLE CLIENT KEY', { action: 'contact' }).then(function (token) {
if ($("#recaptcha_token").length) {
console.log("set new token to existing input >> token = " + token);
$("#recaptcha_token").val(token);
} else {
console.error("recaptcha_token does not exist on email_form");
}
});
}, 60000);
});
</script>
Answer by Dante Michael
/Recibí el error «timeout-or-duplicate» usando ReCaptcha v3,Recibí un formulario de contacto en mi sitio web en Laravel y me gustaría colocar un ReCaptcha v3, pero por ahora el resultado que obtuve de la verificación es el error «timeout-or-duplicate».,Recaptcha no se verifica con file_get_contents,Como se indica en documentación este error es causado por:
Mi cabeza :
<script src="https://www.google.com/recaptcha/api.js?render=My_Site_Key"></script>
<script>
grecaptcha.ready(function () {
grecaptcha.execute('My_Site_Key', { action: 'contact' }).then(function (token) {
var recaptchaResponse = document.getElementById('recaptchaResponse');
recaptchaResponse.value = token;
});
});
</script>
El formulario de contacto:
<form action="{{ route('contact.post') }}" id="contact-form" method="post" name="contactForm">
<input type="hidden" name="_token" id="token" value="{{ csrf_token() }}">
<input type="hidden" name="recaptcha_response" id="recaptchaResponse">
<fieldset>
<div class="col-sm-12">
<input id="name" name="name" placeholder="Nom*" type="text">
</div>
<div class="col-sm-12">
<input id="email" name="email" placeholder="Email*" type="text">
</div>
<div class="col-sm-12">
<input id="object" name="object" placeholder="Objet*" type="text" autocomplete="off">
</div>
<div class="col-xs-12">
<textarea cols="5" id="message" name="message" placeholder="Votre message...*"></textarea>
</div>
<div class="col-xs-12">
<button class="submit active" id="contact-submit">ENVOYER</button>
</div>
<div class="error col-xs-12">
<h3></h3>
</div>
<div class="success col-xs-12">
<h3>Merci ! Votre message a été envoyé !</h3>
</div>
</fieldset>
</form>
Ruta:
Route::post('/contact', array('as' => 'contact.post', 'uses' => '[email protected]'));
El controlador de contacto:
<?php
namespace AppHttpControllers;
use IlluminateHttpRequest;
use AppHttpRequests;
use IlluminateSupportFacadesInput;
use IlluminateSupportFacadesMail;
class ContactController extends Controller
{
public function send() {
$info = array(
'name' => Input::get('name'),
'email' => Input::get('email'),
'object' => Input::get('object'),
'message' => Input::get('message')
);
if($info['name'] == "" || $info['email'] == "" || $info['object'] == "" || $info['message'] == "") {
return json_encode(['response' => 'Tous les champs doivent être remplis !']);
}
if(!filter_var($info['email'], FILTER_VALIDATE_EMAIL)) {
return json_encode(['response' => 'Vous devez rentrer une adresse e-mail valide !']);
}
$ip = Request()->ip();
// Build POST request:
$recaptcha_url = 'https://www.google.com/recaptcha/api/siteverify';
$recaptcha_secret = 'My_Secret_Key';
$recaptcha_response = $_POST['recaptcha_response'];
// Make and decode POST request:
$recaptcha = file_get_contents($recaptcha_url . '?secret=' . $recaptcha_secret . '&response=' . $recaptcha_response);
$recaptcha = json_decode($recaptcha);
// Take action based on the score returned:
if ($recaptcha->score < 0.5) {
return json_encode(['response' => 'Vous êtes considéré comme Bot/Spammer !', 'score' => $recaptcha->score]);
}
Mail::send(['email.html.contact', 'email.text.contact'], ['info' => $info, 'ip' => $ip], function($message) use ($info) {
$message->to('[email protected]')->subject('Bryan Gossuin | Formulaire de contact');
$message->replyTo($info['email'], $info['name']);
});
return json_encode(['response' => 'success','']);
}
}
Finalmente el javascript
$('#contact-form').on('submit', function(e) {
e.preventDefault();
swal({
title: "Souhaitez-vous vraiment envoyer ce mail ?",
icon: "warning",
buttons: {
cancel: {
text: "Annuler",
value: false,
visible: true,
closeModal: true,
},
confirm: "Envoyer",
}
})
.then((value) => {
if (value) {
$.ajax({
method: "POST",
url: "contact",
cache: false,
data: $(this).serialize(),
dataType: 'json',
success: function(json) {
console.log(json.score);
if (json.response == 'success') {
$('#contact-form').trigger("reset");
swal("E-mail envoyé", "Merci de votre demande !", "success");
} else {
swal("Erreur !", json.response, "error");
}
}
}
)
}
});
});
La salida que obtuve de google es
{
"success": false,
"error-codes": [
"timeout-or-duplicate"
]
}
y espero que sea
{
"success": true,
"score" : x,
"error-codes": '',
}
Answer by Rayna Schwartz
3. Marketo would call the validation web service using a webhook after a form was submitted and blacklist any leads for which the response returned success = false. The timestamp would be used for a Change Data Value trigger.,2. Create a simple validation web service that would take an email address and a signature and return a JSON object like this,1. Instead of sending a reCaptcha fingerprint with the form submission, submit a custom signature that would be calculated in the browser based on email address, e.g. sha256 of email address.,Not bulletproof against a determined attacker — they could fake the email signature if they went through the trouble of looking at the form submission code
2. Create a simple validation web service that would take an email address and a signature and return a JSON object like this
{ "success": true|false, // whether the signature matches the email address "response_ts": timestamp // timestamp of the response (ISO format yyyy-MM-dd'T'HH:mm:ssZZ)}
Answer by Esmeralda Harrington
Наконец то javascript
Моя голова :
<script src="https://www.google.com/recaptcha/api.js?render=My_Site_Key"></script>
<script>
grecaptcha.ready(function () {
grecaptcha.execute('My_Site_Key', { action: 'contact' }).then(function (token) {
var recaptchaResponse = document.getElementById('recaptchaResponse');
recaptchaResponse.value = token;
});
});
</script>
Контактная форма :
<form action="{{ route('contact.post') }}" id="contact-form" method="post" name="contactForm">
<input type="hidden" name="_token" id="token" value="{{ csrf_token() }}">
<input type="hidden" name="recaptcha_response" id="recaptchaResponse">
<fieldset>
<div class="col-sm-12">
<input id="name" name="name" placeholder="Nom*" type="text">
</div>
<div class="col-sm-12">
<input id="email" name="email" placeholder="Email*" type="text">
</div>
<div class="col-sm-12">
<input id="object" name="object" placeholder="Objet*" type="text" autocomplete="off">
</div>
<div class="col-xs-12">
<textarea cols="5" id="message" name="message" placeholder="Votre message...*"></textarea>
</div>
<div class="col-xs-12">
<button class="submit active" id="contact-submit">ENVOYER</button>
</div>
<div class="error col-xs-12">
<h3></h3>
</div>
<div class="success col-xs-12">
<h3>Merci ! Votre message a été envoyé !</h3>
</div>
</fieldset>
</form>
Маршрут:
Route::post('/contact', array('as' => 'contact.post', 'uses' => '[email protected]'));
Контактный Контроллер :
<?php
namespace AppHttpControllers;
use IlluminateHttpRequest;
use AppHttpRequests;
use IlluminateSupportFacadesInput;
use IlluminateSupportFacadesMail;
class ContactController extends Controller
{
public function send() {
$info = array(
'name' => Input::get('name'),
'email' => Input::get('email'),
'object' => Input::get('object'),
'message' => Input::get('message')
);
if($info['name'] == "" || $info['email'] == "" || $info['object'] == "" || $info['message'] == "") {
return json_encode(['response' => 'Tous les champs doivent être remplis !']);
}
if(!filter_var($info['email'], FILTER_VALIDATE_EMAIL)) {
return json_encode(['response' => 'Vous devez rentrer une adresse e-mail valide !']);
}
$ip = Request()->ip();
// Build POST request:
$recaptcha_url = 'https://www.google.com/recaptcha/api/siteverify';
$recaptcha_secret = 'My_Secret_Key';
$recaptcha_response = $_POST['recaptcha_response'];
// Make and decode POST request:
$recaptcha = file_get_contents($recaptcha_url . '?secret=' . $recaptcha_secret . '&response=' . $recaptcha_response);
$recaptcha = json_decode($recaptcha);
// Take action based on the score returned:
if ($recaptcha->score < 0.5) {
return json_encode(['response' => 'Vous êtes considéré comme Bot/Spammer !', 'score' => $recaptcha->score]);
}
Mail::send(['email.html.contact', 'email.text.contact'], ['info' => $info, 'ip' => $ip], function($message) use ($info) {
$message->to('[email protected]')->subject('Bryan Gossuin | Formulaire de contact');
$message->replyTo($info['email'], $info['name']);
});
return json_encode(['response' => 'success','']);
}
}
Наконец то javascript
$('#contact-form').on('submit', function(e) {
e.preventDefault();
swal({
title: "Souhaitez-vous vraiment envoyer ce mail ?",
icon: "warning",
buttons: {
cancel: {
text: "Annuler",
value: false,
visible: true,
closeModal: true,
},
confirm: "Envoyer",
}
})
.then((value) => {
if (value) {
$.ajax({
method: "POST",
url: "contact",
cache: false,
data: $(this).serialize(),
dataType: 'json',
success: function(json) {
console.log(json.score);
if (json.response == 'success') {
$('#contact-form').trigger("reset");
swal("E-mail envoyé", "Merci de votre demande !", "success");
} else {
swal("Erreur !", json.response, "error");
}
}
}
)
}
});
});
Вывод, который я получил от google, таков
{
"success": false,
"error-codes": [
"timeout-or-duplicate"
]
}
и я ожидаю, что так оно и будет
{
"success": true,
"score" : x,
"error-codes": '',
}
I’m trying to implement Google reCaptcha v2 on a wizard form. If I complete the captcha and submit the form without having filled out the other fields, it returns result:
{'success': True, 'challenge_ts': '2018-10-30T21:47:25Z', 'hostname': '127.0.0.1'}.
But if I fill out all the required fields so that the form is valid it returns result:
{'success': True, 'challenge_ts': '2018-10-30T21:47:25Z', 'hostname': '127.0.0.1'}.
{'success': False, 'error-codes': ['timeout-or-duplicate']}
[30/Oct/2018 21:47:44] "POST /register/ HTTP/1.1" 200 3912
rendering the form invalid, and throwing an invalid captcha error. I’m guessing that the problem is that the captcha is being validated twice. My verify captcha logic is in the form clean method, so maybe that is the problem:
def clean(self):
cleaned_data = super(Form, self).clean()
recaptcha_response = self.request.POST.get('g-recaptcha-response')
url = 'https://www.google.com/recaptcha/api/siteverify'
values = {
'secret': settings.RECAPTCHA_SECRET_KEY,
'response': recaptcha_response
}
data = urllib.parse.urlencode(values).encode()
req = urllib.request.Request(url, data=data)
response = urllib.request.urlopen(req)
result = json.loads(response.read().decode())
print (result)
if result['success']:
return cleaned_data
else:
raise forms.ValidationError('Error.')
I’m wondering if it’s conflicting with my done method in the view:
def done(self, form_list, **kwargs):
data = {k: v for form in form_list for k, v in form.cleaned_data.items()}
Any ideas on how to fix this?