reCAPTCHA2

D’après Wikipedia « Le terme CAPTCHA est une marque commerciale de l’université Carnegie-Mellon désignant une famille de tests de Turing permettant de différencier de manière automatisée un utilisateur humain d’un ordinateur ».

En gros on veut savoir si on a affaire à un humain ou une machine. Sur Internet ce n’est pas si facile à déterminer mais c’est souvent essentiel pour éviter de se faire spammer ou attaquer en brute force.

Les première versions étaient assez laborieuses et finalement pas si efficaces que ça. Mais notre « ami » Google nous propose désormais une version simple, efficace et plutôt facile à mettre en place. L’objet de cet article est de présenter cette nouvelle version. Je vais le faire en PHP de base pour simplifier la démarche.

Imaginons un site où on veut que les utilisateurs puissent s’inscrire à une lettre d’information. On a bien tout mis en place, que ce soir avec du code propre ou par l’intermédiaire de l’une des nombreuses propositions qui existent en ligne. On crée un joli formulaire et là on ne veut pas qu’un méchant robot vienne saboter notre édifice. Alors on décide de placer un reCAPTCHA et évidemment notre choix se porte sur celui de Google :

La documentation de Google

On trouve une documentation simple mais suffisante sur le site :

Récupérer les clés

Pour utiliser le service il faut une paire de clés en se rendant sur cette page en étant connecté à soin compte Google :

Il faut choisir le type de CAPTCHA qui peut être visible ou invisible :

Pour cet article je vais utiliser le plus classique, le visible.

Quand on a validé on se retrouve avec ses clés :

La mise en place côté client

Elle peut se faire automatiquement (le plus simple) ou de façon explicite si on a une situation particulière. Je vais dans cet article utiliser la première version.

Sur la page où on récupère ses clés on a aussi un résumé de l’intégration côté client :

La mise en place côté serveur

La documentation décrit l’API qui est toute simple et qu’on va voir en action bientôt.

Sur la page où on récupère ses clés on a aussi un résumé de l’intégration côté serveur :

La page HTML et le Javascript

Maintenant qu’on a tous les éléments nécessaires voyons notre exemple d’application.

Voici le code complet :

<!DOCTYPE html>
<html lang="fr">
<head>
    <meta content="text/html; charset=UTF-8" http-equiv="content-type">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0">
    <title>Tes du reCAPTCHA2</title>
    <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.98.2/css/materialize.min.css">
    <link href="https://fonts.googleapis.com/css?family=PT+Sans+Narrow" rel="stylesheet">
    <style>
        body {
            font-family: 'PT Sans Narrow', sans-serif;
            font-size: 18px;
            background-color: #5179d0;
        }
        #captcha {
            margin: 30px 0 20px;
        }
    </style>
</head>
<body>

  <div id="newsletter" class="modal">
      <div class="modal-content">
          <h5 class="center">Pour vous abonner (ou désabonner) à notre lettre d'information :</h5>
          <div class="row">
              <form id="newsform" class="col s12" action="mail.php" method="post">
                  <div class="row">
                      <div class="input-field col s12">
                          <input placeholder="Votre adresse mail" id="email" type="email" name="email" class="validate" required>
                      </div>
                      <div class="input-field col s12">
                          <input name="action" type="radio" value="subscribe" id="subscribe" checked>
                          <label for="subscribe">S'abonner</label>
                          <input name="action" type="radio" value="unsubscribe" id="unsubscribe">
                          <label for="unsubscribe">Se désabonner</label>
                      </div>
                      <div id="captcha" class="input-field col s12 center">
                        <div class="g-recaptcha" data-sitekey="6LeCNyMUAAAAAFXF-onjzpzk7q-XBWDWzpsVkOWM"></div>
                        <div id="error" class="left red-text text-darken-2" style="display: none">
                            Veuillez cliquer sur le Captcha, merci.
                        </div>
                      </div>
                      <button class="btn waves-effect waves-light right" type="submit">Envoyer
                          <i class="material-icons right">send</i>
                      </button>
                  </div>
              </form>
          </div>
      </div>
  </div>

  <div id="erreurmodal" class="modal">
      <div class="modal-content">
          <h5 class="center">Erreur dans la procédure d'abonnement</h5>
          <p class="center-align">Nous sommes désolés mais une erreur inattendue est survenue lors de votre demande d'abonnement. Veuillez s'il vous plait renouveller la procédure dans quelques minutes.</p><p class="center-align">Merci.</p>
      </div>
  </div>

  <div id="subscribemodal" class="modal">
      <div class="modal-content">
          <h5 class="center">Procédure d'abonnement achevée</h5>
          <p class="center-align">Votre demande a bien été prise en compte.</p>
          <p class="center-align">Vous allez recevoir un e-mail de vérification dans un instant. Il contient un lien sur lequel vous devez cliquer afin de confirmer votre inscription.</p>
          <p class="center-align">Merci.</p>
      </div>
  </div>

  <div id="unsubscribemodal" class="modal">
      <div class="modal-content">
          <h5 class="center">Procédure de désabonnement achevée</h5>
          <p class="center-align">Votre demande a bien été prise en compte.</p>
          <p class="center-align">Nous sommes désolés de vous voir partir.</p>
          <p class="center-align">Merci.</p>
      </div>
  </div>

  <div id="intro" class="section scrollspy no-pad-bot center orange-text">
    <div class="container">
      <h1 class="header">Exemple d'utilisation du reCAPTCHA2</h1>
      <div class="row center">
          <p><a id="modalcommand" href="#" class="btn"><i class="material-icons right">email</i>La lettre d'information</a></p>
       </div>
    </div>
  </div>

  <script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.98.2/js/materialize.min.js"></script>
  <script src="https://www.google.com/recaptcha/api.js" async defer></script>
  <script>
      $(function(){

          $('#newsletter').modal()
          $('#subscribemodal').modal()
          $('#unsubscribemodal').modal()
          $('#erreurmodal').modal()

          $('#newsform').submit (function(event) {
              event.preventDefault()
              $.post(
                  $(this).attr('action'),
                  $(this).serialize()
              )
              .done(function(data) {
                  if (data == 200) {
                    $('#newsform').modal('close')
                    if ($('input[name=action]:checked', '#newsform').val() == 'subscribe') {
                        $('#subscribemodal').modal('open')
                    } else {
                        $('#unsubscribemodal').modal('open')
                    }
                  } else if (data == 'nocaptcha') {
                    $('#error').show('slow')
                  } else {
                    $('#newsletter').modal('close')
                    $('#erreurmodal').modal('open')
                  }
              })
          })

          $('#modalcommand').click(function (event) {
              event.preventDefault()
              $('#newsletter').modal('open')
              $('#error').hide()
          })
      })
  </script>

  </body>
</html>

Pour l’esthétique j’ai utilisé Materialize que j’aime bien :

L’intégration du reCAPTCHA se fait en deux emplacements :

  • l’appel de la librairie Javascript :
<script src='https://www.google.com/recaptcha/api.js'></script>
  • l’ajout d’un champ dans le formulaire :
 <div id="captcha" class="input-field col s12 center">
     <div class="g-recaptcha" data-sitekey="6LeCNyMUAAAAAFXF-onjzpzk7q-XBWDWzpsVkOWM"></div>
     <div id="error" class="left red-text text-darken-2" style="display: none">
         Veuillez cliquer sur le Captcha, merci.
     </div>
 </div>

Là je prévoie le signalement de l’erreur au départ invisible si la case n’est pas cochée. Et qui apparaît lorsque ça devient utile :

Lorsqu’on clique sur le bouton le formulaire apparaît dans une fenêtre modale :

Lorsqu’on clique le reCAPTCHA on a évidemment l’apparence classique :

Pour la gestion j’utilise Ajax, donc la soumission et le traitement de la réponse se font avec JQuery :

$('#newsform').submit (function(event) {
  event.preventDefault()
  $.post(
      $(this).attr('action'),
      $(this).serialize()
  )
  .done(function(data) {
      if (data == 200) {
        $('#newsform').modal('close')
        if ($('input[name=action]:checked', '#newsform').val() == 'subscribe') {
            $('#subscribemodal').modal('open')
        } else {
            $('#unsubscribemodal').modal('open')
        }
      } else if (data == 'nocaptcha') {
        $('#error').show('slow')
      } else {
        $('#newsletter').modal('close')
        $('#erreurmodal').modal('open')
      }
  })
})

J’ai prévu 3 modales pour l’abonnement, le désabonnement et l’erreur :

Le traitement côté serveur

Côté serveur on a du simple PHP. Pour la gestion de la requête HTTP j’ai utilisé Guzzle parce que c’est une librairie parfaite pour ne pas se soucier des détails d’implémentation.

Evidemment il faut l’installer :

composer require guzzlehttp/guzzle

Ce qui crée les dossiers avec toutes les dépendances :

Dans notre code il suffira de charger la librairie :

require 'vendor/autoload.php';

Voici le code PHP complet (fichier nommé mail.php) :

<?php

if ($_SERVER['REQUEST_METHOD'] === 'POST') {

    require 'vendor/autoload.php';

    $email = null;
    $action = null;
    $captcha = null;

    if (isset($_POST['g-recaptcha-response'])) {
        $captcha = $_POST['g-recaptcha-response'];
    }
    if (!$captcha) {
        echo 'nocaptcha';
        return;
    }

    if (isset($_POST['email'])) {
        $email = $_POST['email'];
    }
    if (isset($_POST['action'])) {
        $action = $_POST['action'];
    }

    $client = new \GuzzleHttp\Client();

    $response = $client->request (
        'POST',
        'https://www.google.com/recaptcha/api/siteverify', [
        'form_params' => [
            'secret' => 'ma_cle_secrete_cote_serveur',
            'response' => $captcha,
        ]
    ]);

    $responses = json_decode ($response->getBody ());

    if ($responses->success) {
        // Actualisation de l'abonnement sur le serveur
        echo 200;
    } else {
        echo 'fail';
    }
}

Dans le fonctionnement on récupère la valeur du captcha issue du formulaire :

$captcha = $_POST['g-recaptcha-response'];

Si c’est vide on renvoie une erreur :

if (!$captcha) {
     echo 'nocaptcha';
     return;
}

Si c’est bon on envoie la requête chez Google :

$client = new \GuzzleHttp\Client();
$response = $client->request (
     'POST',
     'https://www.google.com/recaptcha/api/siteverify', [
         'form_params' => [
         'secret' => 'ma_cle_secrete_cote_serveur',
         'response' => $captcha,
     ]
]);

$responses = json_decode ($response->getBody ());

if ($responses->success) {
    // Actualisation de l'abonnement sur le serveur
    echo 200;
} else {
    echo 'fail';
}

Il faut évidemment renseigner la clé secrète là.

On récupère la réponse et on adapte le retour en conséquence. Le traitement effectif de l’abonnement se ferait ici.

Conclusion

On voit que l’intégration de reCAPTCHA ne pose aucun problème technique et qu’il serait dommage de ne pas l’utiliser. Mon exemple est assez sommaire côté serveur pour se limiter à l’essentiel de la gestion.

Laisser un commentaire