Използване на Google reCAPTCHA в статичен сайт
Добавяне на reCAPTCHA в статичен сайт
Използването на модерни и многофункционални сайтове, изградени върху динамични CMS платформи като WordPress, днес изглежда без алтернатива. Въпреки това разработването на статични сайтове, базирани на популярни framework библиотеки като Bootstrap, има своите почитатели.
Например, за да добавим reCAPTCHA в WordPress, най-общо казано, трябва да изберем, инсталираме и активираме reCAPTCHA плъгин, в настройките на плъгина да въведем двойката ключове и да посочим къде да се зарежда reCAPTCHA - в страници, публикации и/или коментари.
Докато в Bootstrap (и всички HTML, CSS и JavaScript сайтове) ние създаваме кода на всяка страница на ръка, включително страниците с формуляри, които лесно може да защитим с версия по избор на reCAPTCHA като следваме информацията в статията.
За да тестваме примерите в статията, трябва да създадем три различни регистрации към домейн за всяка версия на reCAPTCHA (checkbox, invisible и v3) и да внимаваме коя двойка ключове ползваме в съответния код. Разбира се, при грешен ключ в кода, в банера на reCAPTCHA ще се зареди грешка.
В статията показваме как се добавя reCAPTCHA във формуляр, как се изпраща информация към reCAPTCHA API, как се валидира с PHP и как работят формулярите в браузър, всичко това по възможно най-опростен и лесен за прилагане начин.
Инструкции за добавяне на reCAPTCHA към сайт
Това е сайта с инструкциите на Google (https://developers.google.com/reCAPTCHA/intro
) как да добавим reCAPTCHA към нашия сайт. Информацията е предназначена за хора, запознати с HTML формуляри, JavaScript, обработване на данни от страна на сървъра. За да инсталирате reCAPTCHA, ще трябва да редактирате кода от примерите.
За да работи която и да е версия на reCAPTCHA коректно, трябва да бъдат изпълнени следните стъпки:
- Frontend интегриране - добавяне на съответната reCAPTCHA (като кликнем в менюто линковете reCAPTCHA v3, Checkbox или Invisible);
- API Request - начините, по които изпращаме данни към API сървъра - различават се за всяка версия;
- API Response - получаване на отговор като JSON обект - описание на данните, които получаваме като резултат от верификацията;
- Verifying the user's response - показва как да потвърдим отговора на потребителя на теста reCAPTCHA challenge.
Информацията е предоставена в предимно описателна форма или таблици с параметри без достатъчно конкретни примери с код, което прави изпълнението на препоръките сложно за хора, които не познават детайлно терминологията на JavaScript.
Добавяне на reCAPTCHA v2 checkbox
reCAPTCHA v2 widget (който съдържа полето за отметка checkbox, откъдето идва името му) може да се зареди автоматично или разделено (explicitly), ако имаме повече от един формуляр на страницата.
Frontend интегриране на reCAPTCHA v2 checkbox
Най-лесният метод за автоматично добавяне на reCAPTCHA widget във формуляра е чрез зареждане на JavaScript API и добавяне на g-recaptcha маркер.
В <head>
секцията на страницата поставяме линк към JavaScript API:
<script src="https://www.google.com/recaptcha/api.js"></script>
Маркерът g-recaptcha е DIV елемент, който съдържа клас g-recaptcha и ключа на сайта в атрибута data-sitekey:
<div class="g-recaptcha" data-sitekey="<?php echo SITE_KEY; ?>"></div>
- class="g-recaptcha" - изпраща като POST параметър g-recaptcha-response отговора на потребителя, след като той реши теста reCAPTCHA;
- data-sitekey="<?php echo SITE_KEY; ?> - ключа на сайта дефинираме като константа SITE_KEY в PHP кода и тук ползваме константата за да не въвеждаме ключа на няколко места в кода, което може да доведе до грешка;
define('SITE_KEY', '_reCAPTCHA_site_key_');
define('SECRET_KEY', '_reCAPTCHA_secret_key_');
Верифициране на потребителския отговор
Следвайки инструкциите, създадохме API POST заявка към https://www.google.com/recaptcha/api/siteverify
с два параметъра като използвахме curl функции:
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://www.google.com/recaptcha/api/siteverify");
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query(array(
'secret' => SECRET_KEY,
'response' => $respKey
)));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
- secret - съдържа секретния ключ от регистрацията;
- response - съдържа информацията от reCAPTCHA в параметъра g-recaptcha-response;
Следващата функция декодира съдържанието на получения JSON низ:
$apiResponse = json_decode($response, true);
Чрез кодът print_r($response);
показваме със син шрифт съдържанието на отговора.
В зависимост от статуса на параметъра success генерираме съобщение за успешен или неуспешен резултат от действието на потребителя.
Как работи reCAPTCHA v2 checkbox?
Следващите изображения ни показват начинът, по който описаните команди работят.
Първоначално зареждане на формуляра с reCAPTCHA v2 checkbox widget:
Въвеждаме име в полето и кликаме в полето I'm not a robot на reCAPTCHA. Зарежда се reCAPTCHA challenge, според инструкциите маркираме правилните полета и кликаме бутона Verify:
reCAPTCHA приема нашият отговор за верен и поставя отметка в полето:
Кликаме бутона Send и се зарежда съответно съобщение от PHP скрипта ($scssmsg = "Вашето име е: '$uname'.";
) - в примера ни показва кое име сме въвели. По същия начин ще се изпълни всеки друг скрипт, въведен в този блок - може да изпратим информацията от полетата на формуляра в имейл, да ги запазим във файл или база данни...
В син цвят се визуализира декодирания от JSON формат отговор:
- success - статус на резултата от проверката (true|false);
- challenge_ts - времето на изпълнение на reCAPTCHA challenge в ISO format;
- hostname - хост името на сайта, където reCAPTCHA challenge е бил изпълнен;
Когато въведем име в полето, но кликнем бутона Send като се опитаме да пропуснем reCAPTCHA, отговора от API скрипта е грешка и ни посочва причината за грешката - missing-input-response (липсва параметър за отговор):
{
"success": false,
"error-codes": [
"missing-input-response"
]
}
Възможните статуси на грешки са описани в таблицата:
Грешка | Описание |
---|---|
missing-input-secret | Не е въведен ключ на reCAPTCHA. |
invalid-input-secret | Ключът е невалиден или неправилно оформен. |
missing-input-response | Липсва параметър за отговор. |
invalid-input-response | Параметърът за отговор е невалиден или неправилно оформен. |
bad-request | Невалидна заявка. |
timeout-or-duplicate | Отговорът не е валиден защото е стар или е използван преди. |
reCAPTCHA е интелигентен алгоритъм, който се самообучава в различаване на легитимните от вредните заявки, затова в много голяма част от тестовете reCAPTCHA challenge не се зареждаше и widget-a директно поставяше отметката в полето I'm not a robot. Съотношението на легитимни / подозрителни заявки се записва като графика в съответния reCAPTCHA профил:
Добавяне на reCAPTCHA v2 invisible
При reCAPTCHA v2 invisible невидимия widget се прикачва към формуляра чрез добавяне на няколко атрибута към HTML бутона:
<button class="g-recaptcha" data-sitekey="<?php echo SITE_KEY; ?>" data-callback="onSubmit">Submit</button>
- class="g-recaptcha" - съдържа информацията от reCAPTCHA в g-recaptcha-response маркер (token);
- data-sitekey - PHP константата SITE_KEY съдържа ключа на сайта и е дефинирана в PHP кода;
- data-callback - съдържа името на функцията за обратно извикване (callback), и се изпълнява, когато потребителя изпрати успешен отговор;
Ако се чудите каква информация и в какъв формат се съдържа в маркера g-recaptcha-response, може да я видим с командата print_r($_POST);
:
Array ( [uname] => name [g-recaptcha-response] => 03AOLTBLRj6CN302DObDBG9sKQgY4ucuWnlU2XrHc76vSVdnlDAFt6Hvy-AegVciIEy7WgtI-wo6lCVRpHaljjwmFYSXTMTPsjeqmhxZiWEHuQ1JdQgqB3vy4JFlrNfnnwhcnHWSZBky5YqG6tkXEiB80k7WiIiOE8n1de3xzFY2JjS1Ygnm0yRIQoCWXLQdjpCzEyfA-UlU24Q7cwuo_L77FqvJRKr7-FH9Gw1D6gnRH7EQVgrdxms1WEX8EoSEU9uxWWVxOyzZ1ciAUMOZore4wdXHcAKib-kNZn6pOVIubQnhWa_qVL_ldbSVf8JnYcsck626lyCua_9E8i5oS20AR-JwcDqAj2tStsoRFGysPPRjethq9mazIJxeCRCh3z0iLL8g6OJhYQlzweItD2mYmjInbJyR2juDruy6IOjbVonwUoDJwU0tUhGs6AKC0XdPkQZKd6ZN1-PkzukdQdVyAfWURX4dlp3iWOb30WkcmdteMzWBai8PQ )
Добавяме към <head>
секцията на HTML кода необходимия JavaScript ресурс:
<script src="https://www.google.com/recaptcha/api.js" async defer></script>
<script>
function onSubmit(t) {
document.getElementById("cform").submit()
}
</script>
Тъй като разликата между reCAPTCHA v2 версиите е само във Frontend интерфейса, който се зарежда в браузъра, няма абсолютно никаква разлика в кода за валидиране от страна на API сървъра.
Как работи reCaptcha v2 invisible?
Първо зареждане на формуляра в браузър - няма го познатият от checkbox версията widget:
Само плъзгащият се банер в долния десен ъгъл подказва, че reCAPTCHA държи под око случващото се на страницата:
Може да променяме местоположението на банера като добавим към бутона атрибут data-badge със стойност bottomright (долу вдясно - по подразбиране), bottomleft (долу вляво) и inline (над бутона). Добавяме и inline CSS чрез style за да има разстояние между банера и бутона:
<button class="g-recaptcha" data-sitekey="<?php echo SITE_KEY; ?>" data-badge="inline" data-callback="onSubmit" style="margin-top: 16px">Submit</button>
Банера е статичен, само линковете Privacy и Terms отвеждат потребителите към съответните страници с техните права, тъй като reCAPTCHA проверява потребителска информация.
Ако кликнем директно бутона получаваме предупреждение Въведете име, но тази функционалност не е част от reCAPTCHA и се програмира допълнително:
Въвеждаме име в текстовото поле и дали ще се зареди reCAPTCHA challenge или не, зависи от оценката на алгоритъма на reCAPTCHA за заявката - легитимна или подозрителна. В по-голяма част от случаите reCAPTCHA challenge не се зарежда:
Получаваме познатия от reCAPTCHA checkbox отговор:
По същия начин работи и системата за грешки - в случая грешката се дължи на дублиране, тъй като рефрешнахме страницата и на практика опитахме да изпратим повторно същата информация:
В нашия reCAPTCHA v2 invisible профил имаме достъп до статистика за сътношението на съмнителните заявки (Suspicious requests) към общия брой заявки (Total sessions):
Добавяне на reCAPTCHA v3 (версия 3)
reCAPTCHA v3 работи във фонов режим като анализира трафика към сайта и предупреждава за подозрителни заявки без да прекъсва сесиите на потребителите с познатите от reCAPTCHA v2 challenges.
На базата на предоставените от reCAPTCHA v3 статистистически данни за трафика, администраторите на сайта имат възможност да предприемат подходящи ответни действия спрямо подозрителните заявки.
Frontend интеграция на reCAPTCHA v3
Най-напред зареждаме JavaScript API с ключа на сайта (site key), който копираме от панела Settings, в <head>
секцията на HTML кода:
<script src="https://www.google.com/recaptcha/api.js?render=<?php echo SITE_KEY; ?>"></script>
След това извикваме функцията grecaptcha.execute като я прикачаме към определено действие (action) или указваме да се изпълнява при зареждане на страницата (onload):
<script>
grecaptcha.ready(function() {
grecaptcha.execute("<?php echo SITE_KEY; ?>", {
action: "homepage"
}).then(function(e) {
document.getElementById("g-recaptcha-response").value = e
})
});
</script>
PHP константата SITE_KEY съдържа ключа на сайта и е дефинирана в началото на PHP кода за валидиране на заявките.
Според инструкциите на Google двата скрипта трябва да бъдат разположени в HTML страницата точно в този ред - първо api.js, след това скрипта, който извиква функцията grecaptcha, за да се избегне конфликт между скриптовете.
За да изпратим информация за формуляра към API сървъра за обработка и връщане на резултат, в HTML кода добавяме скрито поле, което съдържа необходимия маркер (g-recaptcha-response):
<input type="hidden" id="g-recaptcha-response" name="g-recaptcha-response">
Може да видим информацията в маркера по два начина:
Освен чрез познатата команда print_r($_POST);
, информацията в маркера може да видим и с командата console.log(token);
:
<input type="hidden" id="g-recaptcha-response" name="g-recaptcha-response" value="03AOLTBLQyXbVFa2zITKBKukJOCJb__DovyPo9oD6tbN8MpOT22L5JzWi_6Pm_2rSb0RYamWpKCgkV_hnyriOMTcjKE0i6_QodoP1jN7b93gr8hXVn9LKJ5S7xy2fmQietHumayNxuIERUEGoCA7mMQXv4NfB1NP3O0s9dpR4fI5htr5G8th9YrguQIy49I_LFqTJoRLd1W_AbuDrkfJ93eZ1CdGBMNr1Iso_rI8nItZUTYe1R8GcmH6UIi10nye8vwEIDhAQjkfbO2pNlZem74UU_i86OzyqLuq5lo4xbEKT9D7Kk0BLsP3UgFuRbpOdvo_48ChMRcW1r16xMIX0y4f0L7-lqdmxjOG5LjOWw49CUFxzjc6KzYuWoV4hMVbe39gCbP1opWrbYPXnyaY2ih2LlIB3U4JRHJawy5MtZ01j4Uhk9hK1KK5SyutunYIn74diNoQViBuMKbiZhpqZcgQqZN_0WYUTGEA0UbAuzrMfNJknLsuLNniI">
Както и при верификацията на reCAPTCHA v2 следваме стъпките от инструкцията на Google. Отново изпращаме секретния ключ и съдъражанието на маркера g-recaptcha-response към сървъра:
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://www.google.com/recaptcha/api/siteverify");
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query(array(
'secret' => SECRET_KEY,
'response' => $respKey
)));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
$apiResponse = json_decode($response, true);
Въпреки че използваме същите функции за да изпратим същата информация към API сървъра, получения резултат съдържа специфични за reCAPTCHA v3 данни - score (резултат) и action (действия).
Как работи reCAPTCHA v3?
В браузъра се зареждат само елементите на формуляра, reCAPTCHA v3 е невидима и няма никакъв видим белег за физическо присъствие освен наличието на плъзгащия се банер Protected by reCAPTCHA в долния десен ъгъл на екрана. Попълването на текстовото поле Name е задължително като опцията е активирана по избор и не е следствие от действие на reCAPTCHA.
При попълване на полето и кликане на бутона Send се задейства целият reCAPTCHA механизъм и се връща резултата:
- success - показва дали reCAPTCHA token съдържа валидна заявка;
- score - резултат от анализа на заявката (0.0 - 1.0);
- action - действието, към което е прикачена заявката;
- challenge_ts - време на изпълнение на reCAPTCHA;
- hostname - хост името на сайта, от където е получена reCAPTCHA завката;
Именно резултата на параметъра score е ползата от reCAPTCHA v3 - на базата на този резултат се избира стойност, която разделя легитимните от подозрителните заявки. Google препоръчат тази стойност да е 0.5, в нашия пример заявка с резултат score <= 4
води до допълнителни действия, докато заявките с резултат score > 4
се верифицират:
Тези допълнителни действия се въвеждат в блокът на променливата $lowscore
и трябва да бъдат зададени от програмиста на сайта.
if(($apiResponse['success'] == true) AND ($apiResponse['score'] > 0.4)){
$scssmsg = "Вашето име е: '$uname'.";
}
if (($apiResponse['success'] == true) AND ($apiResponse['score'] <= 0.4)){
$lowscore = "Sorry, mr. bot (: ";
}
Това е много важно да се разбере - reCAPTCHA v3 ни предоставя оценка на риска в score, но не предприема никакви действия по отношение на самата заявка. От нас се очаква да програмираме и приведем в изпълнение съответните действия.
reCAPTCHA v3 включва и познатата система от типове грешки error-codes:
Статистика от действията на reCAPTCHA v3 се визуализира в профила и предоставя достъп до следната информация:
Number of requests - показва броя на заявките, които reCAPTCHA е получила от сайта за посоченото действие (action).
Score distribution - показва разпределението на резултатите за посоченото действие. Резултатите варират от 0,0
до 1,0
, където 0,0
показва подозрителен трафик и 1,0
показва легитимен трафик.
Top 10 actions - показва най-добрите 10 действия за сайта. За да получим по-подробна информация за трафика, трябва да посочваме действие на всяка страница, където се изпълнява reCAPTCHA проверка.
Top 10 suspicious traffic actions - показва топ 10 топ действия с подозрителен трафик. Посочването на действие на всяка страница, където се изпълнява reCAPTCHA, проверка ще ни помогне да получаваме по-прецизни анализи на трафика.
Копиране на изходния код от примерите
По-долу може да копирате минимизираният код от примерите. За да възстановите кода в четим вид, препоръчаме да ползвате онлайн услуга като http://beautifytools.com/
. Всеки файл съдържа PHP и HTML код, които части трябва да се разкрасят (това е буквалното име на услугата) отделно една от друга като ползвате разделите HTML Beautifier и PHP Beautifier.
reCAPTCHA v2 Checkbox
<?php define('SITE_KEY', '_reCAPTCHA_SITE_KEY_'); define('SECRET_KEY', '_reCAPTCHA_SECRET_KEY_'); $uname = $_POST['uname']; $respKey = $_POST['g-recaptcha-response']; if ($_POST['submit']) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, "https://www.google.com/recaptcha/api/siteverify"); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query(array( 'secret' => SECRET_KEY, 'response' => $respKey ))); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $response = curl_exec($ch); curl_close($ch); $apiResponse = json_decode($response, true); ($apiResponse['success'] == true) ? ($scssmsg = "Вашето име е: '$uname'.") : ($errmsg = "Неуспешно валидиране."); } ?>
<!DOCTYPE html><html lang="en"><head><title>CHECKBOX</title><script src="https://www.google.com/recaptcha/api.js"></script></head><body><form action="" method="post"><input name="uname" placeholder="Name" required><br><br><div class="g-recaptcha" data-sitekey="<?php echo SITE_KEY; ?>"></div><br><input type="submit" name="submit" value="Send"></form><p style="color:#A50000"><?php echo $errmsg; ?></p><p style="color:#265D0C"><?php echo $scssmsg; ?></p><p><?php echo '<pre style="color:#0055A0; font-size:14px">'.print_r( $response, true ).'</pre>'; ?></p></body></html>
reCAPTCHA v2 Invisible
<?php define('SITE_KEY', '_reCAPTCHA_SITE_KEY_'); define('SECRET_KEY', '_reCAPTCHA_SECRET_KEY_'); $respKey = $_POST['g-recaptcha-response']; $uname = $_POST['uname']; ((!isset($respKey)) and (empty($uname))) ? ($fillerr = '') : ''; ((isset($respKey)) and (empty($uname))) ? ($fillerr = 'Въведете име.') : ''; if ((isset($respKey)) and (!empty($uname))) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, "https://www.google.com/recaptcha/api/siteverify"); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query(array( 'secret' => SECRET_KEY, 'response' => $respKey ))); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $response = curl_exec($ch); curl_close($ch); $apiResponse = json_decode($response, true); ($apiResponse['success'] == true) ? ($scssmsg = "Вашето име е: '$uname'.") : ($errmsg = "Неуспешно валидиране."); } ?>
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>INVISIBLE</title><script src="https://www.google.com/recaptcha/api.js" async defer></script><script>function onSubmit(t){document.getElementById("cform").submit()}</script></head><body><form id="cform" action="" method="post"><input name="uname" placeholder="Name"><br><br><button class="g-recaptcha" data-sitekey="<?php echo SITE_KEY; ?>" data-callback="onSubmit">Submit</button></form><div><p style="color:#A50000"><?php echo $errmsg; ?><?php echo $fillerr; ?></p><p style="color:#265D0C"><?php echo $scssmsg; ?></p><p><?php echo '<pre style="color:#0055A0; font-size:14px">'.print_r( $response, true ).'</pre>'; ?></p></div></body></html>
reCAPTCHA v3
<?php define('SITE_KEY', '6LfYML0UAAAAAAYMQNjBq6JWj8OIl5Id_0PJJFV2'); define('SECRET_KEY', '6LfYML0UAAAAAIEu6kXzUpEzXH6_JB9rF5hqAoeX'); $uname = $_POST['uname']; $respKey = $_POST['g-recaptcha-response']; if ($_POST['submit']) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, "https://www.google.com/recaptcha/api/siteverify"); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query(array( 'secret' => SECRET_KEY, 'response' => $respKey ))); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $response = curl_exec($ch); curl_close($ch); $apiResponse = json_decode($response, true); if ($apiResponse['success'] == false) { $errmsg = "Неуспешно валидиране."; } if (($apiResponse['success'] == true) and ($apiResponse['score'] > 0.4)) { $scssmsg = "Вашето име е: '$uname'."; } if (($apiResponse['success'] == true) and ($apiResponse['score'] <= 0.4)) { $lowscore = "Sorry, mr. bot (: "; } } ?>
<!DOCTYPE html><html lang="en"><head><title>reCAPTCHA</title><script src="https://www.google.com/recaptcha/api.js?render=<?php echo SITE_KEY; ?>"></script></head><body><form id="cform" action="" method="post"><input name="uname" placeholder="Name" required><br><br><input type="hidden" id="g-recaptcha-response" name="g-recaptcha-response"> <input type="submit" name="submit" value="Send"></form><div><p style="color:#A50000"><?php echo $errmsg; ?></p><p style="color:#A50000"><?php echo $lowscore; ?></p><p style="color:#265D0C"><?php echo $scssmsg; ?></p><p><?php echo '<pre style="color:#0055A0; font-size:14px">'.print_r( $response, true ).'</pre>'; ?></p></div><script>grecaptcha.ready(function(){grecaptcha.execute("<?php echo SITE_KEY; ?>",{action:"homepage"}).then(function(e){document.getElementById("g-recaptcha-response").value=e})});</script></body></html>
reCAPTCHA PHP библиотека
Това е PHP библиотека от Google, която обработва от страна на сървъра получените отговори от reCAPTCHA. PHP клиента поддържа както reCAPTCHA v2, така и reCAPTCHA v3.
https://github.com/google/recaptcha
`