Антиспам для FormIt с генерацией случайной строки

Всем привет! Задался вопросом антиспама для форм сайта. Спам сыпался каждый день и надо было это исправлять…
Как будем защищать сайт: Генерируем случайную строку, вставляем в валидацию вызова формы и в саму форму.



Итак, погнали:
  1. Создаём сниппет randString
    <?php 
        $permitted_chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
        $output = 'antispam-'.substr(str_shuffle($permitted_chars), 0, 16);
        return $output;
  2. Добавляем в вызов формы, в параметр &validate
    nobot:contains=^[[randString]]^
  3. Добавляем класс ajax_form форме.
  4. Добавляем скрипт внизу сайта (обычно у меня это чанк [[$scripts]])
    <script>
        $(function(){
            $('.ajax_form').append('<input hidden name="nobot" value="[[randString]]">')
        });
    </script>

Данная статья написана больше для новичков, способ защиты ещё на стадии тестирования и доработки.

И всё) Главное, сниппет [[randString]] вызывать кешируемым!

P.S. Выражаю благодарность @Алексей Смирнов за данный метод защиты, а так же можно просто поставить AjaxFormItLogin где @Артур Шевченко такой метод внедрил.

Поблагодарить автора, а также немного порадовать других активных участников сообщества можно,
отправив донат одним из следующих способов:
Спасибо за внимание!
Денис Усманов
27 апреля 2023, 23:29
modx.pro
6
2 672
+2

Комментарии: 23

Александр Мельник
28 апреля 2023, 07:37
0
Спасибо за очередной способ борьбы со спамом, но честно говоря, я не понял как он работает и почему именно должен защитить от спама.
Вы генерируете случайную строку, вставляете ее в форму через jquery, а затем в валидаторе проверяете ее наличие.
А почему собственно она должна отсутствовать? Спам боты точно так же выполняют весь javascript на странице, как и обычный пользователь и этот инпут будет добавлен в форму и в случае спамбота.
Но если вы говорите что вам помогло, то вам везет и видимо пока что вас атакуют самые примитивные боты)
Я пробовал подобный подход, но не просто append при загрузки странице, а например добавление скрытого поля в форму только после того как кто то кликнул по кнопке, открывающей конкретную форму, пробовал добавлять поле при события наведения мыши на открытой модальное окно и так далее. И в любом случаем через день два спам снова возобновлялся в том же объеме.
Признаюсь, что ни один из способов «автоматического» определения — спам, не спам не дал для меня результата. Лучшего способа с пока что 100 процентным результатом удалось добиться только при таком антиспаме, где пользователю предлагается ответить на вопросы. Как в каптче от гугла — типа выберите все мосты на 8 фотографиях.
    Денис Усманов
    28 апреля 2023, 08:12
    0
    Я согласен с сомнительностью защиты, но как меня уверили, он работает. Я тоже, считаю, что генерация случайной строки на фронте не защитить от серьёзных спам-ботов.
    Надеюсь в ближайшее время, улучшить способ защиты перенеся проверку случайной строки полностью на сторону сервера, пока думаю как это сделать…
    Александр Мельник
    28 апреля 2023, 07:42
    0
    И не совсем мне ясно, в чем ообще смысл сниппета и генерирования каждый раз случайной строки. Ведь валидатор проверяет только факт наличия заполненности такого поля. На мой взгляд, если руками один раз вписать value для этого инпута ничего не измениться. Или я не прав?
      Денис Усманов
      28 апреля 2023, 08:13
      0
      Смысл генерации только в попытке защититься от не очень продвинутых ботов, для которых такой способ защиты непреодолим…
        Александр Мельник
        28 апреля 2023, 08:29
        0
        но согласитесть, нет разницы, будет в каждой форме в этом инпуте написано одна и таже строка (value='hello world') или генерируемая каждый раз разная строка — на антиспам это не должно влиять. Ведь валидатор проверяет только факт заполненности этого поля.
          Денис Усманов
          28 апреля 2023, 08:31
          +1
          Согласен, случайная строка по сути тут как заготовка для более правильного решения… На днях буду прорабатывать этот момент.
            Александр Мельник
            28 апреля 2023, 08:39
            0
            Знаете, я разуверился в возможности написания «атоматического» спамбота, когда не так давно произошло следующее.
            Возникла на работе задача сделать быстрое решени по переносу сайтов с одного места в другое.
            Как ее решал я — писал bash скрипты, которые по ssh выполнли создание архивов, дампы баз, потом подключались на другой сервер, туда все копировали, проводили постобработку типа сброса кеша, замены путей в файлах конфигураций и так далее.
            Как ее решил мой коллега. Написал на jave программу которая тупо запускает браузер и в нем полностью имитирует все действия человека. Тоесть его программа сначала вошла на хостинг откуда нужно перенести, сама там авторизовалась, сама преешла на нужные страницу в панели хостинга, создала архивы, как это делал бы человек, скачала их. ПОтом авторизовалась на другом хостинге и там создала новый сайт, залила туда архив и так далее. Все это визуально у тебя перед глазами, ты только сидишь и смотришь.
            И тут я понял, что при таких возможностях, нет никаких разумных способов отличить бота от человека. Только задавать вопросы боту, на которые ему будет сложно ответить.
      Денис Усманов
      28 апреля 2023, 08:15
      0
      В целом, данный способ ещё на стадии проверки и доработки. Использую его в связке с Google reCaptchaV3.
        Константин Ильин
        28 апреля 2023, 10:00
        +1
        Если вкратце у меня работает так (Подсмотрено у Артур Шевченко, спасибо ему)
        1. тоже генерируется рандомная строка
        2. тоже проверяется через contains
        3. и полю nobot эта рандомная строка присвоится только(!) при наведении на форму, а у вас как я понял сразу.

        У меня полет нормальный
        {set $secret = md5(rand(0,999999999))}
        {'!AjaxForm' | snippet :[
            ....
            'secret' => $secret ,
            'validate' => 'vscval:contains=^'~$secret~'^'
            ....
        ]}

        //В форме
        <input type="hidden" name="vscval" style="display:none" data-vscval="{$_pls['secret']}">

        //Js
        function watchForms(){
            if(document.querySelector('form')){
                document.querySelectorAll('form').forEach((ff) => {
                    ff.addEventListener('mousemove', (e) => {
                        if(ff.querySelector('input[name="vscval"]')){
                            let inpSecret = ff.querySelector('input[name="vscval"]'),
                                inpSecretData = inpSecret.getAttribute('data-vscval');
                            if(inpSecret.value == ''){
                                inpSecret.value = inpSecretData;
                            }
                        }
                    });
                });
            }
        }
        watchForms()
          Денис Усманов
          28 апреля 2023, 10:04
          0
          Тоже хотел сделать именно при наведении, но решил, напишу сначала простенькую статью. А там глядишь люди дадут хорошие советы, и я перепишу статью… Благодарю! За это утро, мне уже несколько человек дали хорошие решения, которые я обязательно тут покажу!
            Добрый день. Как себя ведёт событие mousemove на мобильных устройствах?
              Константин Ильин
              30 мая 2023, 21:40
              +1
              На телефоне все норм, сейчас проверил на одном из сайтов форма отправляется. Ну если что можно на событие touchmove повесить
            Эдуард
            28 апреля 2023, 13:00
            0
            На многих сайтах я делал простой append (точнее prepend) в форму с фиксированной строкой, не заморачивался с генерацией случайно строки.
            Спама было мало, но тоже подтверждаю на некоторых сайтах спам-боты пробивали и сыпался спам, тут как повезет. Так как способ простой, то ставил в формы по умолчанию на все сайты. Защищает от ботов, которые не умеют в JavaScript (интересно много таких сейчас?)
              Руслан Алеев
              28 апреля 2023, 13:15
              0
              В FormIt есть встроенная защита от спама, в виде математического уравнения, см. docs.modx.com/current/ru/extras/formit/formit.hooks/math Но мало видел, что ее используют, хотя, думаю, что нормально работает.
                Павел Гвоздь
                02 мая 2023, 10:50
                0
                Неужели способ «создать поле email и требовать, чтобы оно всегда было пустым» уже не работает?)
                  Денис Усманов
                  02 мая 2023, 10:57
                  0
                  Даже не знаю) Не пробовал так делать.
                  На данный момент скажу вот что, данный метод, что я описал в статье, действительно работает. На сайте где я его внедрил, спам как отрезало. Но я понимаю, что метод хоть и работает, но он ещё не доработан. Мне мой хороший товарищ Дмитрий Волинский дал способ защиты с записью случайной строки в сессию и отлавливанием её прехуком… Буду проверять такой метод на другом проекте, и как будут «плоды», опишу способ реализации в этой статье.
                    Дмитрий
                    23 мая 2023, 13:45
                    0
                    Второй месяц уже пошёл, вроде полёт нормальный, у тебя как проходит?
                      Денис Усманов
                      23 мая 2023, 14:21
                      +2
                      Всё тихо, спамеров не видно ?
                        Pandemic
                        24 ноября 2023, 11:59
                        0
                        А продолжение будет? интересная затейка
                    Futuris
                    07 мая 2023, 10:10
                    +1
                    У меня работает уже второй месяц, с тех пор как узнал про него. До этого был просто ужас!))
                    Николай Савин
                    12 июля 2023, 13:19
                    0
                    Изобрел CSRF токен и радуется
                    Дмитрий
                    30 июля 2024, 13:22
                    0
                    Можно немного усложнить задачу для спамеров на стандартном синтаксисе. Т.к. случайная строка генерируется и не меняется при обновлении страницы. Решить этот вопрос можно через плейсхолдер. Я делал форму на FetchIt, поэтому приведу код на нём:

                    [[!randString:toPlaceholder=`randString`]]
                    [[!FetchIt?
                        &snippet=`FormIt`
                        &submitVar=`popup`
                        &form=`popup.form.tpl`
                        &emailTpl=`emailForm.tpl`
                        &hooks=`FormItSaveForm,email,sendTelegram`
                        &emailSubject=`Заявка на обратный звонок`
                        &emailTo=`[[++email_to]]`
                        &emailFrom=`test@test.ru`
                        &randString=`[[!+randString]]`
                        &validate=`antispam:contains=^[[!+randString]]^,name:required,phone:required,popup_request_conditions:required`
                        &validationErrorMessage=`Необходимо заполнить все поля формы!`
                        &successMessage=`Спасибо! Ваша заявка отправлена!`
                        &errTpl=`Необходимо согласиться с обработкой персональных данных`
                    ]]

                    В чанке popup.form.tpl необходимо добавить в форму скрытое поле:

                    <input type="hidden" name="antispam" style="display:none" data-antispam="[[!+randString]]">

                    ну и также прикрутил js отсюда modx.pro/howto/23926#comment-139070
                      Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
                      23