Integrar reCAPTCHA v2 con PHP en formulario
En este post te enseñaré a integrar el servicio llamado reCAPTCHA v2 con PHP, el cual es un simple captcha o checkbox de “no soy un robot” en un formulario HTML.
También te enseñaré a verificarlo con PHP del lado del servidor.
El servicio que vamos a integrar es reCAPTCHA v2, el cual funciona en varios lenguajes de servidor, pues se consume usando HTTP; hoy veremos cómo hacerlo en PHP.
Veremos un ejemplo del formulario y de la comprobación en el lado del servidor.
También dejaré una demostración. Al final tendremos algo como lo del gif:
Requisitos y recomendaciones
En este punto asumo que ya tienes tu par de claves (de sitio y secreta) generadas. Si no las tienes o no sabes cómo obtenerlas, mira este post.
Las peticiones las haremos con file_get_contents
y stream_context_create
, si no sabes mucho sobre las mismas te recomiendo leer este post.
Nota: a través del post explicaré todo el código, pero si en el futuro hago una actualización la haré en GitHub en su respectivo repositorio. Recomiendo seguirme para estar al tanto 😉
El lado del cliente
1 – Cargar script
Debemos cargar el script de reCAPTCHA v2. El mismo se encarga de inyectar el input
en el formulario. Al momento de escribir este post está:
https://www.google.com/recaptcha/api.js
Y la forma de integrarlo es la siguiente:
<script src="https://www.google.com/recaptcha/api.js" async defer></script>
2 – El div
Cuando hayamos cargado el script, debemos indicar al captcha en dónde debe inyectar la casilla de “no soy un robot”. Eso se logra con un div
:
<div
class="g-recaptcha"
data-sitekey="TU_CLAVE_DE_SITIO_VA_AQUÍ">
</div>
En el atributo data-sitekey
debes colocar tu clave de sitio.
No coloques aquí la secreta, coloca la de sitio.
También asegúrate de dos cosas sobre el div
:
- Colocarle la clase
g-recaptcha
- Ponerlo dentro del formulario que vas a enviar. Es decir, entre las etiquetas
<form>
y</form>
En mi caso el formulario completo queda así:
<!doctype html>
<html lang="es">
<!--
Programado por Luis Cabrera Benito
____ _____ _ _ _
| _ \ | __ \ (_) | | |
| |_) |_ _ | |__) |_ _ _ __ _____| |__ _ _| |_ ___
| _ <| | | | | ___/ _` | '__|_ / | '_ \| | | | __/ _ \
| |_) | |_| | | | | (_| | | / /| | |_) | |_| | || __/
|____/ \__, | |_| \__,_|_| /___|_|_.__/ \__, |\__\___|
__/ | __/ |
|___/ |___/
Blog: https://parzibyte.me/blog
Ayuda: https://parzibyte.me/blog/contrataciones-ayuda/
Contacto: https://parzibyte.me/blog/contacto/
-->
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1,
shrink-to-fit=no">
<meta name="description" content="Formulario con reCAPTCHA v2 by
parzibyte">
<meta name="author" content="Parzibyte">
<title>Formulario con reCAPTCHA v2 by parzibyte</title>
<!-- No olvides cargar el script -->
<script src="https://www.google.com/recaptcha/api.js" async defer></script>
</head>
<body>
<h2 class="text-center">Bienvenido de nuevo</h2>
<form action="procesar.php" method="POST">
<label for="correo">Correo</label>
<br>
<input id="correo" name="correo"
type="email"
placeholder="Correo electrónico">
<br><br>
<label for="palabraSecreta">Contraseña</label>
<br>
<input id="palabraSecreta" name="palabraSecreta"
type="password"
placeholder="Contraseña">
<br>
<br>
<!--
A continuación colocamos el div en donde será
inyectado el input
-->
<div
class="g-recaptcha"
data-sitekey="6LdILLQUAAAAALrX0WgjoQSCeomm5EVh27p_a7Qg">
</div>
<br>
<button type="submit" class="btn btn-primary mb-2">
Entrar
</button>
</form>
</body>
</html>
Fíjate en la línea 28 que es en donde incluimos el script, y en la línea 51 que es en donde ponemos el div
.
Además del captcha, podemos poner otros input
s como en el ejemplo. El action
del formulario es procesar.php cuyo código veremos a continuación.
El formulario se ve así hasta ahora:
Lado del servidor – Verificar token de reCAPTCHA v2 con PHP
Como lo dije, el div
inyectará un dato en el formulario, cuyo name
será g-recaptcha-response
y al que podemos acceder en PHP a través de:
$_POST["g-recaptcha-response"]
Ese es un token que debemos comparar en la API que Google ofrece. El endpoint para comparar está en:
https://www.google.com/recaptcha/api/siteverify
Debemos mandar dos argumentos obligatorios:
secret
– Nuestra clave secretaresponse
– El token deg-recaptcha-response
Aparte de eso se puede enviar la IP del usuario, pero no es obligatorio.
Obviamente debemos comprobar si esa clave existe en $_POST
, ya que si no, significa que el usuario no resolvió el captcha. Además de eso, debemos comprobarlo con la API porque que esté presente no significa que sea correcto.
<?php
/*
Integrar reCAPTCHA v2 con PHP
https://parzibyte.me/blog
Programado por Luis Cabrera Benito
____ _____ _ _ _
| _ \ | __ \ (_) | | |
| |_) |_ _ | |__) |_ _ _ __ _____| |__ _ _| |_ ___
| _ <| | | | | ___/ _` | '__|_ / | '_ \| | | | __/ _ \
| |_) | |_| | | | | (_| | | / /| | |_) | |_| | || __/
|____/ \__, | |_| \__,_|_| /___|_|_.__/ \__, |\__\___|
__/ | __/ |
|___/ |___/
Blog: https://parzibyte.me/blog
Ayuda: https://parzibyte.me/blog/contrataciones-ayuda/
Contacto: https://parzibyte.me/blog/contacto/
*/
# Aquí pon la clave secreta que obtuviste en la página de developers de Google
define("CLAVE_SECRETA", "TU_CLAVE_VA_AQUÍ");
# Comprobamos si enviaron el dato
if (!isset($_POST["g-recaptcha-response"]) || empty($_POST["g-recaptcha-response"])) {
exit("Debes completar el captcha");
}
# Antes de comprobar usuario y contraseña, vemos si resolvieron el captcha
$token = $_POST["g-recaptcha-response"];
$verificado = verificarToken($token, CLAVE_SECRETA);
# Si no ha pasado la prueba
if ($verificado) {
/**
* Llegados a este punto podemos confirmar que el usuario
* no es un robot. Aquí debes hacer lo que se deba hacer, es decir,
* comprobar las credenciales, darle acceso, etcétera, pues
* ya ha pasado el captcha
*/
echo "Has completado la prueba :)";
} else {
exit("Lo siento, parece que eres un robot");
}
/**
* Verifica el token del captcha y regresa true o false
* true en caso de que el usuario haya pasado la prueba
* false en caso contrario
*
* Más información: https://parzibyte.me/blog/2019/08/21/peticion-http-php-json-formulario/
*
* @author parzibyte
* @see https://parzibyte.me/blog
*/
function verificarToken($token, $claveSecreta)
{
# La API en donde verificamos el token
$url = "https://www.google.com/recaptcha/api/siteverify";
# Los datos que enviamos a Google
$datos = [
"secret" => $claveSecreta,
"response" => $token,
];
// Crear opciones de la petición HTTP
$opciones = array(
"http" => array(
"header" => "Content-type: application/x-www-form-urlencoded\r\n",
"method" => "POST",
"content" => http_build_query($datos), # Agregar el contenido definido antes
),
);
# Preparar petición
$contexto = stream_context_create($opciones);
# Hacerla
$resultado = file_get_contents($url, false, $contexto);
# Si hay problemas con la petición (por ejemplo, que no hay internet o algo así)
# entonces se regresa false. Este NO es un problema con el captcha, sino con la conexión
# al servidor de Google
if ($resultado === false) {
# Error haciendo petición
return false;
}
# En caso de que no haya regresado false, decodificamos con JSON
# https://parzibyte.me/blog/2018/12/26/codificar-decodificar-json-php/
$resultado = json_decode($resultado);
# La variable que nos interesa para saber si el usuario pasó o no la prueba
# está en success
$pruebaPasada = $resultado->success;
# Regresamos ese valor, y listo (sí, ya sé que se podría regresar $resultado->success)
return $pruebaPasada;
}
He creado la función verificarToken
que regresa true
o false
dependiendo del éxito al completar el captcha.
Solo tienes que remplazar la clave en la línea 25 o invocar al método con tu clave secreta y el token. En este caso debes colocar tu clave secreta.
Lo que se hace es hacer una petición HTTP de tipo POST y esperar un JSON, para más tarde decodificarlo. Ese JSON tiene una estructura así:
{
"success": true,
"challenge_ts": "2019-08-21T21:12:29Z",
"hostname": "localhost"
}
La propiedad success
estará en true
o false
. Si hay errores también se mostrarán aquí. Por ejemplo:
{
"success": false,
"error-codes": ["invalid-input-response"]
}
En caso de que no quieras manejar los errores simplemente checa la propiedad success
.
Si deseas conocer más información sobre los errores y la verificación del token siempre tienes a tu disposición la documentación oficial.
Demostración y código completo
Si quieres ver la demostración, haz click aquí. En caso de que quieras el código fuente completo, lo encuentras en mi GitHub.
reCAPTCHA v2 con PHP y Bootstrap 4
Este captcha se integra en cualquier formulario. Para hacerlo más bonito visualmente lo colocaré dentro de un formulario de login, basado en un ejemplo anterior de Bootstrap en donde se coloca el correo y la contraseña para iniciar sesión.
Quiero recalcar que los estilos no importan, solo importa que coloquemos el div
dentro de la etiqueta form
. Dentro del formulario pueden estar todos los campos, pues el script de reCAPTCHA se encarga de inyectar un campo oculto.
<!doctype html>
<html lang="es">
<!--
Programado por Luis Cabrera Benito
____ _____ _ _ _
| _ \ | __ \ (_) | | |
| |_) |_ _ | |__) |_ _ _ __ _____| |__ _ _| |_ ___
| _ <| | | | | ___/ _` | '__|_ / | '_ \| | | | __/ _ \
| |_) | |_| | | | | (_| | | / /| | |_) | |_| | || __/
|____/ \__, | |_| \__,_|_| /___|_|_.__/ \__, |\__\___|
__/ | __/ |
|___/ |___/
Blog: https://parzibyte.me/blog
Ayuda: https://parzibyte.me/blog/contrataciones-ayuda/
Contacto: https://parzibyte.me/blog/contacto/
-->
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1,
shrink-to-fit=no">
<meta name="description" content="Formulario con Bootstrap 4 y reCAPTCHA v2 by parzibyte">
<meta name="author" content="Parzibyte">
<title>Formulario con Bootstrap 4 y reCAPTCHA v2 by parzibyte</title>
<script src="https://www.google.com/recaptcha/api.js" async defer></script>
<!-- Cargar el CSS de Boostrap-->
<link
href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
rel="stylesheet">
</head>
<body>
<main role="main" class="container my-auto">
<div class="row">
<div id="login" class="col-lg-4 offset-lg-4 col-md-6 offset-md-3
col-12">
<h2 class="text-center">Bienvenido de nuevo</h2>
<img class="img-fluid mx-auto d-block rounded"
src="https://picsum.photos/id/870/300/200" />
<form action="procesar.php" method="POST">
<div class="form-group">
<label for="correo">Correo</label>
<input id="correo" name="correo"
class="form-control" type="email"
placeholder="Correo electrónico">
</div>
<div class="form-group">
<label for="palabraSecreta">Contraseña</label>
<input id="palabraSecreta" name="palabraSecreta"
class="form-control" type="password"
placeholder="Contraseña">
</div>
<div class="form-group">
<div
class="g-recaptcha"
data-sitekey="6LdILLQUAAAAALrX0WgjoQSCeomm5EVh27p_a7Qg">
</div>
</div>
<button type="submit" class="btn btn-primary mb-2">
Entrar
</button>
<br>
<a href="#">Contraseña olvidada</a>
</form>
</div>
</div>
</main>
</body>
</html>
El action
del formulario es igualmente procesar.php así que el lado del servidor no se cambia. Ahora el formulario se ve así:
Demostración con Bootstrap 4
Puedes probarlo en este enlace.
Conclusión
Así de fácil es agregar protección contra bots a nuestros sitios; ya sea para páginas de votaciones, accesos a sistemas, formularios de contacto, etcétera.
reCAPTCHA solo indica si el usuario es un ser humano, es tu responsabilidad manejar todos los datos y hacer validaciones. Si quieres validar con PHP te recomiendo Valitron.
No olvides que cuando acabes de hacer pruebas debes eliminar el dominio localhost
de tu panel de administración.
Finalmente te invito a leer más sobre PHP, Seguridad y consumo de APIs en mi blog.
MUCHAS GRACIAS!! estuve buscando esto durante días y no me salía. Fue de muchisima ayuda!
Hola amigo, eh intentado de todo y aun no eh podido logar que se envien mis datos utilizando el recaptcha, verifico el recaptcha y presiono el boton de enviar y me aparece el mensaje de Lo siento, parece que eres un robot, ya verifique mis claves y el sitio web que sea el correcto pero aun asi no me funciona será que actualmente hayan cambiado algunas cosas, verifique el codigo que tienes el github pero aun asi me sigue apareciendo lo mismo, será que puedas ayudarme?
Hola buen día. Con gusto te ayudo, más información aquí: https://parzibyte.me/blog/contrataciones-ayuda/
Saludos 🙂
Amigo gracias! pase por varios videos y tutoriales y no lograba verificar mi captcha… Pero con tu codigo todo funciona a la perfeccion, tanto en local como en mi hosting. Gracias Totales
Me agrada ayudar. No olvides seguirme y compartir 🙂
Saludos