Использование Google reCAPTCHA v2 и v3 в одной форме

—

от автора

Введение

У Google появилась замечательная reCAPTCHA v3. Замечательна она тем, что освобождает пользователя от необходимости тыкать по картинкам в поисках светофоров и пожарных гидрантов, при этом вполне успешно анализирует его действия на сайте и оценивает их по шкале от 0 до 1. Чем выше балл, тем качественней наш бот выше вероятность, что пользователь реальный. Обычно выставляют порог равным 0.5.

А что делать, если хочется добра пользователям сайта, но есть у его владельца некая обеспокоенность, что не пропустит невидимая столь ценного реального пользователя, который может и ведет себя странно и подозрительно, но совсем не бот?

Ответ — последовательно использовать капчу обоих версий — не прошел невидимую проверку — появилась видимая капча!

Что делаем

Тут обозначу основные моменты. Весь код есть по ссылке на github. достаточно переименовать файл config.example.php в config.php и поменять в нем ключи доступа на настоящие.

Для подключения необходимо получить публичные (site key) и приватные (secret key) ключи от обеих версий Google reCAPTCHA на сайте.

Добавляем на страницу скрипт, указав в параметре публичный ключ версии 3

<script src="https://www.google.com/recaptcha/api.js?render=KEY_SITE_V3"></script>

Создаем форму, в которую помимо наших полей добавляем два скрытых для токенов, а также один блок, в который будет добавляться видимая капча. Блоку требуется задать значение атрибута «id».

<input type="hidden" name="captcha_token_v2"> <input type="hidden" name="captcha_token_v3"> <div id="captcha"></div>

Форма у нас будет отправляться по ajax. Пишем обработчик для события подтверждения формы. Тут:

  • form — эта переменная содержит объект формы

  • captcha_key_site_v3 — открытый публичный ключ для капчи 3 версии

  • token — токен, присвоенный пользователю, по нему в дальнейшем будем обращаться в сервис Google и получать те самые баллы

— открытый который проверяет, что определена переменная grecaptcha из библиотеки Google, получает токен и записывает его в скрытое поле, после чего отправляем форму.

form.submit(function (e){   e.preventDefault()   if (typeof grecaptcha != 'undefined' && typeof captcha_key_site_v3 != 'undefined') {     grecaptcha.ready(function () {       grecaptcha.execute(captcha_key_site_v3, {action: 'submit'}).then(function (token) {         if(token) {           form.find('input[name="captcha_token_v3"]').val(token)           sendForm()         }       })     })   } })

Для отправки ипользуется метод sendForm, в котором выполняется такая последовательность действий:

  1. Отправляем ajax запрос с данными формы на сервер

  2. Если на сервере проверка пройдена и данные формы приняты, получаем сообщение об успехе и выводим информацию об этом пользователю

  3. Если произошла ошибка, связанная с версией 3 (не отчечает сервер или недостаточно баллов), то рендерим капчу версии 2

function sendForm() {                 let data = form.serialize()                 $.ajax({                     type: 'POST',                     url: form.attr('action'),                     data: data,                     dataType: 'json',                     success: function (response) {                         if(response.success) {                             // показываем сообщение об успехе и перезагружаем страницу                             $('.alert').text(response.text).slideDown()                             setTimeout(function (){location.reload()}, 4000)                         } else if(response.error) {                             // если была ошибка капчи, сбрасываем капчку v2 при наличии                             if (widgetCaptcha !== false) {                                 grecaptcha.reset(widgetCaptcha)                             }                             // если ошибка была в версии v3, показываем видимую капчу v2                             // widgetCaptcha - идентификатор, т.е. можно рендерить и управлять несколькими штуками                             if (response.error === 'fall_captcha_v3' && !widgetCaptcha) {                                 widgetCaptcha = grecaptcha.render('captcha', {                                     'sitekey': captcha_key_site_v2,                                     'theme': 'dark',                                     'callback': setTokenV2                                 })                             }                         }                     }                 })             }

Методу render передаем первым параметром id блока, в который будет помещена капча, вторым — набор параметров:

  • sitekey — публичный ключ капчи версии 2

  • theme — цветовая схема капчи, может принимать значения dark / light

  • callback — имя функции, которая будет вызвана после проверки капчи версии 2

WidgetCaptcha — это переменная, в которой хранится идентификатор капчи, полезно использовать особенно когда на странице несколько форм, для сброса капчи используеся метод grecaptcha.reset(WidgetCaptcha)

В функции-коллбеке setTokenV2 передается токен от второй версии капчи, его мы записываем во второе скрытое поле

function setTokenV2(token) {   form.find('input[name="captcha_token_v2"]').val(token) }

В этой же функции можно сразу вызвать sendForm() или предоставить пользователю самостоятельно нажать на кнопку. На этот раз форма улетает уже с двумя токенами, при этом первым проверяется токен версии 2, на всякий случай на стороне сервера по нему тоже получаем инофрмацию от Google, и если все в порядке, возвращаем информацию об успехе в браузер, где и показывем соответстующее сообщение.

Код серверной части в файле ajax.php:

<?php require_once 'config.php'; session_start(); if(!empty($_POST) && $_POST['sid'] == session_id()) {     $token_v2 = $_POST['captcha_token_v2'];     $token_v3 = $_POST['captcha_token_v3'];     $result = checkCaptcha($token_v2, $token_v3);     die(json_encode($result, true)); }  function checkCaptcha($token_v2 = false, $token_v3 = false) {     // если не передано ни одного токена - возвращаем ошибку     if (!$token_v3 && !$token_v2) {         return ['error' => 'fall_captcha'];     }     // если дело дошло до капчи второй версии     elseif ($token_v2) {         // проверяем информацию по второй версии, если google ответил, что провека успешная - возвращаем успех         $result = checkCaptchaCurl($token_v2, KEY_SECRET_V2);         if (!$result['success']) {             // если проверка провалилась - тоже ошибка             return ['error' => 'fall_captcha_v2'];         }     }     // если токен второй версии еще не получен, но есть 3, значит проверяем невидимую капчу     else {         $result = checkCaptchaCurl($token_v3, KEY_SECRET_V3);         // проверяем количество очков от 0 до 1. Чем ближе к 1, тем больше вероятности, что это человек         if ($result['score'] < 1) {             return ['error' => 'fall_captcha_v3'];         }     }     // возвращаем успех, если проверки пройдены     $text = 'Your message "' . $_POST['text'] . '" was send to email "' . $_POST['email'] . '" successfully';     return ['success' => true, 'text' => $text]; }  /**  * Метод для отправки запроса в google через CURL  * @param $response  * @param $secret  * @return mixed  */ function checkCaptchaCurl($response, $secret) {     $url_data = 'https://www.google.com/recaptcha/api/siteverify' . '?secret=' . $secret . '&response=' . $response . '&remoteip=' . $_SERVER['REMOTE_ADDR'];     $curl = curl_init();     curl_setopt($curl, CURLOPT_URL, $url_data);     curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);     curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);     $captcha_res = curl_exec($curl);     curl_close($curl);     $captcha_res = json_decode($captcha_res, true);     return $captcha_res; }  

Надеюсь, кому-то пригодиться в работе, также буду рад комментариям и вопросам.


ссылка на оригинал статьи https://habr.com/ru/articles/582984/