php

reCAPTCHA v2 con PHP – Ejemplo de integración

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:

Formulario de Bootstrap 4 con reCAPTCHA v2 y PHP

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 inputs 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:

Formulario con captcha

 

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:

  1. secret – Nuestra clave secreta
  2. response – El token de g-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.

Estoy aquí para ayudarte 🤝💻


Estoy aquí para ayudarte en todo lo que necesites. Si requieres alguna modificación en lo presentado en este post, deseas asistencia con tu tarea, proyecto o precisas desarrollar un software a medida, no dudes en contactarme. Estoy comprometido a brindarte el apoyo necesario para que logres tus objetivos. Mi correo es parzibyte(arroba)gmail.com, estoy como@parzibyte en Telegram o en mi página de contacto

No te pierdas ninguno de mis posts 🚀🔔

Suscríbete a mi canal de Telegram para recibir una notificación cuando escriba un nuevo tutorial de programación.
parzibyte

Programador freelancer listo para trabajar contigo. Aplicaciones web, móviles y de escritorio. PHP, Java, Go, Python, JavaScript, Kotlin y más :) https://parzibyte.me/blog/software-creado-por-parzibyte/

Ver comentarios

  • 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?

  • 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

Entradas recientes

Creador de credenciales web – Aplicación gratuita

Hoy te voy a presentar un creador de credenciales que acabo de programar y que…

2 días hace

Desplegar PWA creada con Vue 3, Vite y SQLite3 en Apache

Ya te enseñé cómo convertir una aplicación web de Vue 3 en una PWA. Al…

1 semana hace

Arquitectura para wasm con Go, Vue 3, Pinia y Vite

En este artículo voy a documentar la arquitectura que yo utilizo al trabajar con WebAssembly…

1 semana hace

Vue 3 y Vite: crear PWA (Progressive Web App)

En un artículo anterior te enseñé a crear un PWA. Al final, cualquier aplicación que…

1 semana hace

Errores de Comlink y algunas soluciones

Al usar Comlink para trabajar con los workers usando JavaScript me han aparecido algunos errores…

1 semana hace

Esperar promesa para inicializar Store de Pinia con Vue 3

En este artículo te voy a enseñar cómo usar un "top level await" esperando a…

1 semana hace

Esta web usa cookies.