WordPress: Блокирование ботов с помощью Яндекс-капчи

Вред ли есть человек, которому нравится, что его используют. Да, все мы используем друг друга в той или иной мере, но в большинстве случаев это происходит по обоюдному согласию. А вот когда ваш сайт начинают использовать как пастбище для выгула ботов, при этом вы от этого не только не получаете никакого профита, но и в этом случае сайт начинает проседать в рейтинге из-за отказов, это уже подстава.
В одной из прошлых заметок я описал шаги для блокирования поведенческих ботов. Данный метод основывался на блокировании некоторого диапазона IP-адресов, а также, даже в большей степени, скрытие счётчиков метрики. Не все боты блокировались успешно, поэтому я решил добавить к данному решению Яндекс-капчу. Кроме успешного блокирования ботов на вашем сайте, капча использует облако, чтобы боты, которые засветились и не прошли проверку, были бы забанены у других.

Начало работы с капчей

Для начала нам нужно зарегистрировать аккаунт в Облаке Яндекс. Для этого создадим Yandex ID и залогинимся с помощью него на странице Yandex SmartCaptcha.

Использовать капчу можно бесплатно, если у вашего сайта не больше 250’000 посещений в месяц (~8300 в сутки).

 

Хоть использование капчи и бесплатное (в большинстве случаев), всё равно придётся привязать платёжную карту. Для проверки, с неё спишут 11 рублей, потом сразу вернут. Хотя, даже если вам удастся на немного превысить допустимый бесплатный лимит, то цены очень и очень дешёвые:

Например, если цена за 1000 запросов в месяц составляет 100,00 ₽, а за месяц был сделан 250 001 запрос, стоимость за месяц составит:
(250 001 — 250 000)/1000 * 100 ₽ = 0,1 ₽

Моему сайту ещё очень далеко даже до 5000 посетителей в сутки, так что мне всё это удовольствие получается бесплатно.

Теперь создадим и настроим капчу.
1. В консоли управления выбираем каталог.
2. Находим сервис Yandex SmartCaptcha.
3. Нажимаем кнопку Создать капчу.
4. Вводим имя капчи:

Длина — от 2 до 63 символов;
Может содержать строчные буквы латинского алфавита, цифры и дефисы;
Первый символ — буква, последний — не дефис.

 

5. Выбираем тип основного задания, которое предлагается решить пользователю (нам предлагается чекбокс «Я не робот» или слайдер).
6. Выбираем тип дополнительного задания (нам на выбор предложат распознавание текста, силуэты или калейдоскоп).
7. Указываем сложность.
8. Указываем список сайтов, на которых будет размещаться капча.
9. Внешний вид я оставил стандартным.
10. Нажимаем кнопку Создать.

Получение ключей капчи

11. В консоли управления выбираем каталог.
12. Находим сервис Yandex SmartCaptcha.
13. Нажмимаем на имя капчи или в левой части страницы нажимаем пункт ⚐ Обзор
14. Нам потребуется значение поля Ключ клиента — скопируйте его куда-нибудь, позже мы вернёмся к нему.
*. Если вы планируете использовать свой сервер для проверки, вам потребуется значение поля Ключ сервера — также скопируйте его куда-нибудь.

Добавление виджета на страницу

В прошлый раз мы изменяли файл header.php, я немного изменю код, его нужно добавить после следующей строки (так что эту строчку не повторяйте):

<div id="main" class="wrapper">
wp-content/themes/twentytwelve-child/header.php
  
	<div id="main" class="wrapper">

<div class="before-footer-scripts-place"></div>
  <div class="welcome-pt-modal">
      <div class="welcome-pt-overlay">
          <div class="site-popup-inner welcome-pt-message">
              <form method="post" enctype="multipart/form-data" action="">
                  <div class="site-form-title">Добро пожаловать!</div>
                  <div class="site-row">
                      <p class="site-form-text">В связи с наплывом ботов приходится собирать IP-адреса каждого
                          посетителя! Если вы с этим не согласны, просто закройте страницу.</p>
                  </div>
                  <div class="site-form-buttons">
                      <div class="site-form-button">
                          <div style="height: 100px" id="captcha-container" class="smart-captcha">
                          </div>
                      </div>
                  </div>
              </form>
          </div>
      </div>
  </div>

<script type="text/javascript">
  var yaParams = {IP: "<? echo $_SERVER['REMOTE_ADDR']; ?>"};
</script>

<script src="/detect/pts.lazyload.js"></script>

<script src="https://smartcaptcha.yandexcloud.net/captcha.js?render=onload&onload=smartCaptchaInit" defer></script>
В этом участке кода я убрал кнопку и вместо неё сразу поставил капчу.

 

Объединённый скрипт блокировки

Странное название, зато честное :) Здесь я объединил логику из скрипта pts-lazyload от Пиксель Тулс с кодом Яндекс-капчи. (Хотя может это просто имя префикса файла pts-lazyload совпадает c названием PixelTools и к Пиксель Тулс отношения не имеет).

/detect/pts.lazyload.js
function callback(token)
{
    if (token)
    {
        loadMetrics(2);
    } else
    {
        console.log("->callback(token): wrong token");
    }
}
function smartCaptchaInit()
{
    console.log("-> smartCaptchaInit()")
    loadMetrics(1);
    if (!window.smartCaptcha)
    {
        return;
    }
    window.smartCaptcha.render('captcha-container', {
        sitekey: 'КЛЮЧ_КЛИЕНТА',
        hl: 'ru',
        callback: callback,
    });
}
function smartCaptchaReset()
{
    if (!window.smartCaptcha)
    {
        return;
    }
    window.smartCaptcha.reset();
}
function smartCaptchaGetResponse()
{
    if (!window.smartCaptcha)
    {
        return;
    }
    var resp = window.smartCaptcha.getResponse();
    console.log(resp);
    alert(resp);
}
function loadMetrics(need_check)
{
    const gtag = 'UA-123456789-1';
    const ytag = '12345678';
    let dataLazyLoadingJS = {
        data: {
            // https://tagassistant.google.com/
            // Google tags found: UA-123456789-1
            ga_counter: {
                status: false,
                html: `<script async src="https://www.googletagmanager.com/gtag/js?id=${gtag}"><\/script><script>window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date());  gtag('config', '${gtag}');<\/script>`,
                area: 'head'
            },
            ya_counter: {
                status: false,
                html: `<script type="text/javascript">(function(m,e,t,r,i,k,a){m[i]=m[i]||function(){(m[i].a=m[i].a||[]).push(arguments)}; m[i].l=1*new Date(); for (var j = 0; j < document.scripts.length; j++) {if (document.scripts[j].src === r) { return; }} k=e.createElement(t),a=e.getElementsByTagName(t)[0],k.async=1,k.src=r,a.parentNode.insertBefore(k,a)}) (window, document, "script", "https://mc.yandex.ru/metrika/tag.js", "ym"); ym(${ytag}, "init", {params:window.yaParams, clickmap:true, trackLinks:true, accurateTrackBounce:true, webvisor:true }); <\/script> <noscript><div><img src="https://mc.yandex.ru/watch/${ytag}" style="position:absolute; left:-9999px;" alt="WordPress: Блокирование ботов с помощью Яндекс-капчи" /><\/div><\/noscript>`,
                area: 'head'
            }
        }
    };
    let dataSettings = {
        cookie_name: 'PST_VERIFIED_COOKIE',
    };
    let LazyLoad = new ptsLazyLoad(dataLazyLoadingJS, dataSettings);
    LazyLoad.simpleCheck(need_check);
}

class ptsLazyLoad
{
    constructor(dataLazyLoadingJS, dataSettings)
    {
        this.dataLazyLoadingJS = dataLazyLoadingJS;
        this.dataSettings = dataSettings;
    }
    lazyLoadingJS(type, area)
    {
        if (this.dataLazyLoadingJS['data'][type]['status'] === false)
        {
            this.dataLazyLoadingJS['data'][type]['status'] = true;

            const render = (relEl, tpl) =>
            {
                const range = document.createRange();
                range.selectNode(relEl);
                const child = range.createContextualFragment(tpl);
                return relEl.appendChild(child);
            };
            render(area, this.dataLazyLoadingJS['data'][type]['html']);
        }
    }
    loadAllDataScripts()
    {
        for (let key in this.dataLazyLoadingJS['data'])
        {
            this.lazyLoadingJS(key, document.querySelector(this.dataLazyLoadingJS['data']['area']));
        }
    }
    showMessage()
    {
        let modal = document.querySelector('.welcome-pt-overlay');
        modal.classList.add('is-active');
    }
    hideMessage()
    {
        let that = this;
        let modal = document.querySelector('.welcome-pt-overlay');
        modal.classList.remove('is-active');
        that.cookieSet();
        that.loadAllDataScripts();
        setTimeout(function ()
        {
            modal.style.display = 'none';
        }, 300);
    }
    isSearchSystemBotSigns()
    {
        let uaList = [
            'APIs-Google', 'Mediapartners-Google', 'AdsBot-Google-Mobile', 'AdsBot-Google', 'Googlebot', 'AdsBot-Google-Mobile-Apps',
            'YandexBot', 'YandexMobileBot', 'YandexDirectDyn', 'YandexScreenshotBot', 'YandexImages', 'YandexVideo', 'YandexVideoParser',
            'YandexMedia', 'YandexBlogs', 'YandexFavicons', 'YandexWebmaster', 'YandexPagechecker', 'YandexImageResizer', 'YandexAdNet',
            'YandexDirect', 'YaDirectFetcher', 'YandexCalendar', 'YandexSitelinks', 'YandexMetrika', 'YandexNews', 'YandexCatalog',
            'YandexMarket', 'YandexVertis', 'YandexForDomain', 'YandexSpravBot', 'YandexSearchShop', 'YandexMedianaBot', 'YandexOntoDB',
            'YandexOntoDBAPI', 'YandexVerticals', 'Mail.RU_Bot', 'StackRambler', 'Yahoo', 'msnbot', 'bingbot', 'PixelTools', 'PixelBot'
        ];
        let sBrowser = false, sUsrAg = navigator.userAgent;
        for (let i = 0; i < uaList.length; i += 1)
        {
            if (sUsrAg.indexOf(uaList[i]) > -1)
            {
                sBrowser = true;
                break;
            }
        }

        return sBrowser;
    }
    cookieCheck()
    {
        return document.cookie.indexOf(this.dataSettings.cookie_name) > -1;
    }
    cookieSet()
    {
        const date = new Date();
        date.setTime(`${date.getTime()}${(365 * 30 * 24 * 60 * 60 * 1000)}`);
        let expiryDate = `expiryDate=" ${date.toUTCString()}`;
        document.cookie = `${this.dataSettings.cookie_name}=true; ${expiryDate}; path=/`;
    }
    simpleCheck(need_check)
    {
        if (+need_check === 1 && !this.cookieCheck() && !this.isSearchSystemBotSigns())
        {
            this.showMessage();
        }
        else if (+need_check === 2)
        {
            this.hideMessage();
        }
        else
        {
            this.loadAllDataScripts();
        }
    }
}

Ближе к началу скрипта, в функции smartCaptchaInit необходимо прописать переменную КЛЮЧ_КЛИЕНТА из п.14 (одинарные кавычки оставляем):

window.smartCaptcha.render('captcha-container', {
    sitekey: 'КЛЮЧ_КЛИЕНТА',
    hl: 'ru',
    callback: callback,
});

Ближе к середине, в этих строчках задаём данные счётчика Яндекс-метрики и Гугл-аналитики:

const gtag = 'UA-123456789-1';
const ytag = '12345678';

Я их вынес в константы, чтобы уменьшить риск внести ошибку, если вы случайно где-то их забудете поставить в коде. Кроме этого указал принудительное использование русского языка для виджета (hl: ‘ru’).

 

Файл стилей

Чтобы всё пряталось/отображалось когда нужно, необходимо добавить файл стилей welcome-pt.css. Здесь я не стал удалять лишние правила для работы с кнопкой, которую больше не использую, а оставил — вдруг кому пригодится.

/detect/welcome-pt.css
/* Modal */
.welcome-pt-overlay {
  opacity: 0;
  visibility: hidden;
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  background-color: rgba(0, 0, 0, .5);
  z-index: 8000;
  transition: all 0.3s;
}
.welcome-pt-message {
  opacity: 0;
  visibility: hidden;
  width: 100%;
  max-width: 500px;
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 8001;
  box-shadow: 0px 11px 15px -7px rgb(0 0 0 / 20%), 0px 24px 38px 3px rgb(0 0 0 / 14%), 0px 9px 46px 8px rgb(0 0 0 / 12%);
  text-align: center;
  padding: 5px;
  margin: 16px 0;
  border-radius: 4px;
  background-color: #fff;
  box-sizing: border-box;
  transition: all 0.3s;
}
@media (max-width: 532px) {
  .welcome-pt-message {
    width: auto;
    left: 16px;
    right: 16px;
    transform: translate(0, -50%);
  }
}
.is-active.welcome-pt-overlay,
.is-active .welcome-pt-message {
  opacity: 1;
  visibility: visible;
}

/* Custom styles here */
.site-form-title {
  text-align: center;
  margin-bottom: 20px;
  font-size: 36px;
  line-height: 40px;
  font-weight: 300;
  color: #3d424b;
}
@media (max-width: 532px) {
  .site-form-title {
    margin-bottom: 14px;
    font-size: 30px;
    line-height: 36px;
  }
}
.site-form-text {
  font-size: 16px;
  line-height: 20px;
  margin-top: 0;
}
.site-form-buttons {
  display: flex;
  justify-content: center;
  flex-direction: column;
  gap: 15px;
  margin-top: 32px;
}
.welcome-pt-close {
  height: 50px;
  line-height: 50px;
  min-width: 180px;
  padding: 0 32px;
  border: none;
  font-size: 16px;
  white-space: nowrap;
  color: #fff;
  cursor: pointer;
  text-decoration: none;
  border-radius: 5px;
  background-color: #0836ff;
  transition: all 0.3s ease 0s;
  display: inline-block;
  margin: 0;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  box-sizing: border-box;
  text-align: center;
  box-shadow: 0px 3px 1px -2px rgb(0 0 0 / 20%), 0px 2px 2px 0px rgb(0 0 0 / 14%), 0px 1px 5px 0px rgb(0 0 0 / 12%);
}
.welcome-pt-close:hover:enabled {
  background-color: #0435c9;
}
.welcome-pt-close:disabled {
  background: #8399f7;
  pointer-events: none;
}
.welcome-pt-close:hover:disabled {
  background: #8399f7;
  pointer-events: none;
}

Как всегда, если будут вопросы или вы найдёте ошибку, пишите в комментариях!



Подписаться
Уведомление о
guest
0 Комментарий
Inline Feedbacks
View all comments