Work Hours
8:00 am - 6:00 pm UTC+3

Elena Sarkisova

Bitrix and reCapcha. Fighting robots, not humans

This article was translated automatically. We are working over improving the translation. 
Please send your questions about the article to info@intervolga.ru

What is a captcha and what are the problems with it?

What is a “captcha” today even a schoolboy knows. But “why” - site owners are well aware.

Any captcha is a double-edged sword. A complex captcha protects well against spam robots, but it also prevents customers from sending an application or order. Simple - on the contrary, it is convenient for people and vulnerable to robots.


To the rescue, with the motto “Just for people, difficult for robots”, hurries Google reCapthca .

Looking ahead, I’ll say that we’ve made our cool / universal / non-programming module to replace the standard captcha with reCaptcha .

Principle of operation Google captcha

reCAPTCHA is a small widget. For most users, it looks like a simple ticked box.


In some cases, it will take a small task to prove that you are not a robot. For example, Google may ask you to choose from a list of images with a specific object, or select the desired object in one image:


Whether you are required to complete such a task or not depends on what Google knows about you.

If your actions on the Internet are similar to human (you read different sites, are authorized in the mail, enter search queries) - most likely you will not need to complete the quest.


The work of most captchas is based on the task of text recognition. Robots plus or minus learned to solve them. Google reCaptcha 2.0 went further and uses image recognition tasks. While robots guess images much worse than people.

How to connect reCAPTHCA to Bitrix

We would like to find such a magic pill that will solve this problem. But our research shows that while it is not. Therefore, in addition to reviewing MarketPlace solutions, we will describe our vision of the architecturally correct options for such integration.

Solutions from MarketPlace

Marketplace offers several solutions to connect recaptcha. Among them are paid and free. All promise about the same functionality: enabling and disabling recaptcha throughout the site in just one click. You only need to register your domain on the site  www.google.com/recaptcha and enter 2 received keys in the module settings.

The most pressing issues when connecting reCAPTCHA:

  • Multiple Captch support per page.

  • Captcha in a popup window that potentially loads over AJAX.

How do the Marketplace solutions do these tasks?

We checked on the example of a couple of the most popular (by the number of installations) solutions that you can try without buying.

Module “ reCaptchaFree

Available settings:

  • multisite support - you can configure recaptcha for each site separately;

  • the ability to disable recaptcha without removing the module. The truth here was a problem - when deactivating recaptcha using this setting, js scripts were still connected on the page;

  • setting the visual display of the widget, etc.

I was pleased that the module was working on forms in a modal window and an AJAX form. However, for some components to work in ajax mode, you need to make changes to the template code, which is inconvenient if there are many such components on the site.

Also, after installing the module, you will need to edit the component template to get rid of such situations:


As it turned out later, this must be done for all modules.

Module “ Google ReCaptcha – improved captcha and bot protection

The composition of the settings is small: only the keys. Therefore, to disable recaptcha, you will have to remove the module.

As for performance, the module coped well with ordinary forms. But with some components in Ajax mode caused errors.

Other modules (for example, Google reCAPTCHA | advanced captcha ) from MarketPlace do not provide an opportunity to test.

How to make yourself?

If for some reason we are not satisfied with ready-made solutions or we need to recaptcha only in one form on the site, then we can connect it ourselves. We know 3 correct ways to do this.

Custom component and component template

Captcha is displayed in the templates of some components. The correctness of its input is checked in the component. Why is that? - Because:

  • So architecturally correct. MVC and all that.

  • The component template is cached and not always executed.

Consequently, without customization of the component is not enough.

Before customizing a component, it is convenient to create a separate class for working with captcha.

class GoogleReCaptcha

{

public static function getPublicKey() { return ‘your_public_key’;}

public static function getSecretKey() { return ‘your_secret_key’;}

/**

   * @return array - if validation is failed, returns an array with errors, otherwise - empty array. This format is expected by component.

   */

  public static function checkClientResponse()

  

      $context = \Bitrix\Main\Application::getInstance()->

getContext();

      $request = $context->getRequest();

      $captchaResponse = $request->getPost("g-recaptcha-response");

      if($captchaResponse)

      

          $url = ' https://www.google.com/recaptcha/api/siteverify ';

          $data = array('secret' => static::getSecretKey(), 'response'

=> $captchaResponse);

          $httpClient = new HttpClient();

          $response = $httpClient->post($url,$data);

          if($response)

              $response = \Bitrix\Main\Web\Json::

decode($response,true);

          if(!$response['success']) {

              return $response['error-codes'];

          

          return array();

      

      return array(‘no captcha response’);

  

}

 

Example of connecting the recaptcha to the registration form

We use the bitrix component: main.register. This component provides a standard captcha.

Copy the component into our namespace of the / bitrix / components / directory. Find in the component code input captcha check.

Fragment from file component.php:

// check captcha

   if ($arResult["USE_CAPTCHA"] == "Y")

   

       if (!$APPLICATION->CaptchaCheckCode($_REQUEST["captcha_word"], $_REQUEST["captcha_sid"]))

           $arResult["ERRORS"][] = GetMessage("REGISTER_WRONG_CAPTCHA");

   

In this fragment, we replace the standard captcha test with our recaptcha check Google:

if ($arResult["USE_CAPTCHA"] == "Y")

   

       $errorMessages = GoogleReCaptcha::checkClientResponse();

if( !empty($errorMessages)))

           $arResult["ERRORS"][] = $errorMessages;

   

Then edit the component template. Replace the output of standard captcha to recaptcha:

Fragment from file template.php:

<div class="g-recaptcha" data-sitekey="<?=GoogleReCaptcha::getPublicKey()?>">

Changes only component is not enough to work captcha. You also need to connect the script to interact with the service. This should be done in the component_epilog.php file of the component.

\Bitrix\Main\Page\Asset::getInstance()->addString(

“<script src=' https://www.google.com/recaptcha/api.js '></script>”);

As a result, we get:

Connecting Recaptas with JS

You can try to go the other way, so you do not have to customize dozens of components:

  1. When displaying the page in the browser, we will find and substitute all bitrix captcha for reCAPTCHA.

  2. We will catch the filling of forms on any page (all POST hits). If there is a user-defined reCAPTHCA in the request, let the bitrix think that the user has solved the bitrix-captcha.

This is what the JS part will look like using jQuery.

//Find all bitrix captcha

b_captcha_sid = $("[name='captcha_sid']");

if (b_captcha_sid.length > 0) {

  b_captcha_img = $(b_captcha_sid).siblings('img');

  if(b_captcha_img.length >0) {

//Remove images

      $(b_captcha_img).remove();

      $(b_captcha_sid).each(function (index) {

         //Put in place of images recaptcha

          tmp = document.createElement('div');

          tmp.innerHTML = "<div class='g-recaptcha' data-sitekey='your_site_key'></div>";

          g_recaptcha = tmp.firstChild;

          g_recaptcha = (this.parentElement).

appendChild(g_recaptcha);

         //Drawing a recaptcha widget

          if(typeof (grecaptcha) != "undefined") {

              grecaptcha.render(g_recaptcha,{

                  'sitekey': 'your_site_key'

              });

          

      });

  

}

//Hide all the input fields of the word captcha

b_captcha_word = $("[name='captcha_word']");

if (b_captcha_word.length > 0) {

  $(b_captcha_word).hide();

}


Components on the page do not know anything about Google captcha and when sending the form will check the correctness of the input standard. Accordingly, we need to simulate its input.

HTTP request data components are obtained from superglobal variables $_POST, $_GET, $_REQUEST. To check the captcha component expects variables captcha_sid и captcha_word. captcha_word when sending form with recaptcha will be empty. We take it from the table b_captcha.

It is necessary to change superglobal variables before working off components. For this you can use the events that are called during the execution of the page: OnPageStart or OnBeforeProlog.

Вот так будет выглядеть PHP-часть:

$context = \Bitrix\Main\Application::getInstance()-&gt;

getContext();

$request = $context->getRequest();

$captchaResponse = $request->getPost("g-recaptcha-response");

if($captchaResponse)

{

if(!\GoogleReCaptcha::checkClientResponse())

$captchaSid = $request->getPost("captcha_sid");

    if($captchaSid)

    

//Because no API allows you to get the word captcha by ID, we make a query directly to the database

       $dbRes = \Bitrix\Main\Application::getConnection()->query(

"SELECT CODE FROM b_captcha WHERE id='".$captchaSid."'");

       if($res = $dbRes->fetch())

       

          if($res['CODE'])

          

             $_REQUEST['captcha_word'] = $_POST['captcha_word'] = $res['CODE'];

          

       

    

}


If the user entered the recaptcha and successfully passed it, then we substitute the correct value captcha_word. Otherwise, when checking the captcha component will give an error.

Connecting Recaptcha with OnEndBufferContent

We can replace captcha not only on the client side using JS, but also on the server side. To do this, use the page execution event again  - OnEndBufferContent .

Example of replacing captcha on the server side:

public static function OnEndBufferContent(&$content)

{

//For forms loaded via ajax, add a call to the recaptcha rendering script

$renderScript = "";

$htmlId = "";

$context = \Bitrix\Main\Application::getInstance()->getContext();

$request = $context->getRequest();

if($request->isAjaxRequest())

    $id = uniqid ('r_');

    $renderScript = "<script>renderRecaptcha('$id');</script>";

    $htmlId = 'id="'.$id.'"';

//Remove the field to enter the word

$content = preg_replace( '/<input[^<>]*name\s?=\s?.captcha_word.[^<>]*>/' , '' , $content );

//All images are replaced by recaptcha

$content = preg_replace ( '/<img[^<>]*src\s?=\s?.\/bitrix\/tools\/captcha\.php\?(captcha_code|captcha_sid)=[^<>]*>/' , "<div class='g-recaptcha' data-sitekey=''your_site_key'' ".$htmlId."></div>".$renderScript ,  $content);

}


The render function for JavaScript re-caps looks like this:

renderRecaptcha = function(elementId) {

if (typeof (grecaptcha) != "undefined") {

    var element = document.getElementById(elementId);

    if(element != undefined && element.childNodes.length == 0) {

       grecaptcha.render(element, {

          'sitekey': 'your_site_key'

       });

    

}

It should be added to the site template.


Further verification of the captcha is carried out as in the second method - by event OnPageStart or OnBeforeProlog .

Error messages

When replacing captchas at once on the whole site (methods 2 and 3), we will need to change the message about the error captcha. To avoid such situations:


All messages are recorded in language variables. We can override them globally. Create a file /bitrix/php_interface/user_lang/ru/lang.php.

For the component for adding and editing an information block element, an entry in the file will look like this:

$MESS['/bitrix/components/bitrix/iblock.element.add.form/lang/ru/component.php']['IBLOCK_FORM_WRONG_CAPTCHA'] = "Verification from auto-fill failed";

Conclusions

Using Google recaptcha has indisputable advantages compared to the standard bitrix captcha: life of users is greatly simplified, and the security of the site does not suffer.

The task does not look very difficult. Nevertheless, in solutions found at MarketPlace, it is solved with flaws. We have fixed this annoying situation and present you our module for replacing captcha Bitrix on Google reCaptcha.   


This article was translated automatically. We are working over improving the translation. 
Please send your questions about the article to info@intervolga.ru
  • 18.07.2019