Наверх

Защита от спама формы на AjaxForm

У FormIt есть стандартный функционал защиты от спама — проверка любого поля на пустоту. Если робот-спамер заполнит такое поле, то письмо не будет отправлено.

Единственный минус этого способа в том, что ответ от сервера приходит с ошибкой валидации — по ответу сразу видно, что письмо не отправлено и нужно робота перенастроить.

В итоге такой способ задерживает меньше спама, чем мог бы.

Обычно на сайтах я использую связку FormIt + AjaxForm. Вот небольшое решение для фильтрации спама на этой связке. Оно похоже на стандартную валидацию с одним лишь условием — если поле заполнено и письмо решено не отправлять, спамеру сервер ответит сообщением с успехом отправки. В итоге спам-робот и его хозяин будут считать, что отправка работает, а у клиента на почте спама не будет.

Добавляем в форму поле, по которому будем фильтровать
<input type="text" name="surname" class="form-input" placeholder="Фамилия">

Прячем её стилями
input[name="surname"] {
    display: block;
    width: 2px;
    height: 3px;
    margin-bottom: -3px;
    opacity: 0.01;
}

Создаём сниппет checkSpam
<?php
if ($_POST['surname']) { // проверяем наше поле на пустоту
    echo $AjaxForm->success('Ваше сообщение отправлено');
    die();
} else {
    return true;
}

Добавляем наш сниппет в качестве хука перед email
'hooks' => 'checkSpam,email',

Теперь если письмо не отправлено, спамеру всё равно будет показано сообщение об успехе.


18 комментариев

  1. Denis Efremov 08 августа 2017, 13:00 # 0
    А я сначала на клиенте чекаю
    import extend from 'extend'
    
    /**
     * Class Validator.
     *
     * @class      Validator
     */
    export default class Validator {
    
        /**
         * Constructs the object.
         *
         * @param      {Object}  config  The configuration
         */
        constructor (config) {
            if (config === undefined) {
                config = this.defaults
            }
    
            this.config = extend(this.defaults, config)
        }
    
        /**
         * Get the default config
         *
         * @return     {Object}
         */
        get defaults () {
            return {
                styles: {
                    success: {
                        borderColor: '#4CAF50',
                        boxShadow: 'inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(76, 175, 80, 0.6)',
                    },
                    error: {
                        borderColor: '#E91E63',
                        boxShadow: 'inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(233, 30, 99, 0.6)',
                    },
                },
                rules: {
                    name (value) {
                        return value.length > 1
                    },
                    phone (value) {
                        return value.match(/\+7 \(\d\d\d\) \d\d\d-\d\d-\d\d/)
                    }
                }
            }
        }
    
        /**
         * Pass the validation
         *
         * @param      {HTMLElement}  el
         */
        pass (el) {
            this.setStyle(el, this.config.styles.success)
        }
    
        /**
         * Fail the validation
         *
         * @param      {HTMLElement}  el
         */
        fail (el) {
            this.setStyle(el, this.config.styles.error)
        }
    
        /**
         * Check the value
         *
         * @param      {Event}  e
         */
        check (e) {
            return this.config.rules[e.target.name](e.target.value)
                ? this.pass(e.target)
                : this.fail(e.target)
        }
    
        /**
         * Sets the style.
         *
         * @param      {HTMLElement}  el
         * @param      {Object}       styles  The styles
         */
        setStyle (el, styles) {
            Object.keys(styles).forEach(prop => {
                el.style[prop] = styles[prop]
            })
        }
    }
    
    И юзаю
            const vally = new Validator(),
    
            validate = (e) => {
                vally.check(e)
            },
    
            initForm = () => {
                textMask({
                    inputElement: phoneInput,
                    mask: ['+', '7', ' ', '(', /[1-9]/, /\d/, /\d/, ')',
                        ' ', /\d/, /\d/, /\d/, '-', /\d/, /\d/, '-', /\d/, /\d/],
                })
    
                phoneInput.removeEventListener('keyup', validate)
                phoneInput.addEventListener('keyup', validate)
    
                nameInput.removeEventListener('keyup', validate)
                nameInput.addEventListener('keyup', validate)
            }
    
    1. Илья Уткин 08 августа 2017, 13:16 # 0
      Тут смысл именно в том, чтобы эмулировать отправку для спамера — чтобы он не догадался, что его вычислили.
      1. Denis Efremov 08 августа 2017, 19:22 # 0
        Покажи где я блокирую отправку.
        Я меняю стили…
    2. Павел 16 августа 2017, 18:41 # 0
      А для formit как будет?
      1. Илья Уткин 17 августа 2017, 09:22 # 0
        Для FormIt у меня готового решения нет.
      2. Роман 24 августа 2017, 10:09 # 0
        Илья, зачем стили прописывать к инпуту, если можно просто сделать так
        <input type="hidden" name="surname" class="form-input" placeholder="Фамилия">
        
        1. Илья Уткин 24 августа 2017, 10:45 # 0
          Да, можно и так. Но часто роботы такие скрытые инпуты не трогают и вся схема не работает. Причём часто даже инпуты, скрытые с помощью display: none; тоже не заполняются, поэтому такие сложные стили.
          1. Руслан 08 октября 2017, 01:58 # 0
            Здравствуйте, Илья, а в Formit 3.0.3 работает? По-моему не очень, поправьте меня, если ошибаюсь.
            Заранее спасибо.
            1. Илья Уткин 08 октября 2017, 09:24 # 0
              Это не работает без AjaxForm
              1. Руслан 08 октября 2017, 15:09(Комментарий был изменён) # 0
                Нет, я про связку FormIt и AjaxForm. Сделал дважды на разных сайтах, с разницей в несколько месяцев, на одном работает, на другом — нет. На последнем была новая версия FormIt, вот я и сомневаюсь.
                1. Илья Уткин 09 октября 2017, 08:09 # 0
                  хм… не знаю, тут не могу сказать, пока не сталкивался со сложностями… всё может быть
        2. Яна 11 октября 2017, 23:06 # 0
          А я делала так, изначально поле делаю не пустым,
          JS при загрузке его очищаю
          и проверяю если поле НЕ пустое — значит это спамер (ну или JS налажал)))
          1. Илья Уткин 12 октября 2017, 08:12 # 0
            Да, тоже хороший вариант. Тут главное — чтобы у каждого было хоть немного, но по-разному.
          2. Алексей 20 августа 2018, 13:55(Комментарий был изменён) # 0
            Здравствуйте.
            Пробую Ваш способ.
            При попытке отправки формы со страницы с заполненным полем «surname» (имитирую заполнение ботом) браузер выдает следующее:
            Fatal error: Call to a member function success() on a non-object in /путь_к_сайту/core/cache/includes/elements/modsnippet/35.include.cache.php on line 3
            Захожу в этот файл и вижу, что это полностью сниппет checkSpam, 3-я строка, соответственно:
            echo $AjaxForm->success('Ваше сообщение отправлено');
            Означает ли это, что AjaxForm не отрабатывает и сообщение отправителю не ушло?
            p.s. Я не разработчик, поэтому прошу прощение за возможное непонимание элементарных процессов)
            p.p.s. AjaxForm установлен.
            1. Vitaliy Chamin 06 сентября 2018, 16:14 # 0
              Илья, а как быть, если человек пользуется автозаполнением от браузеров? Это поле по-умолчанию заполняется и сообщения админу не приходят.
              1. Илья Уткин 07 сентября 2018, 08:44 # 0
                Ну да, проблема. Тогда нужно переименовать поле surname во что-то другое. Правда в этом случае и спамеры его могут перестать заполнять.
                1. Роман Смирнов 29 марта 2019, 00:33(Комментарий был изменён) # 0
                  Попробуйте отключить автозаполнение с помощью атрибута autocomplete=«off».
                  Однако, это не всегда может помочь, т.к. автозаполнение отключается в браузере сейчас.
                  <input autocomplete="off" name="surname"/>
                2. Dmitry 18 января 2024, 03:27 # 0
                  Оставьте все как есть, просто добавьте скрытый чекбокс с условием not, то отправки не будет, соответственно роботы его могут распознать а люди нет, и хорошо что автозаполнение на чекбоксы не работают. И соответственно один чекбокс что б видно было, что б можно было его нажать при отправке формы человеком.

                  Авторизация

                  через сервис Loginza:


                  Шаблоны MODX

                  1 2 Дальше »

                  Объектная
                  модель
                  MODX