- Способ 1: использование хуков FormIt (spam и math)
- hooks spam
- (hooks math)
- Способ 2: использование своих скрытых полей
- Вариация 1.
- Вариация 2 (под AjaxForm).
- Вариация 3 (под AjaxForm).
- Способ 3 — в основном от ручного спама ну и от части роботов
- Способ 4 — генерация ключа (для AjaxForm)
- Способ 5 — ограничение на количество вводимых символов
- Способ 6 -генерацией случайной строки
- Вариация 1
- Вариация 2 (на fenom)
- Прочие способы
Сегодня разберем пару методов борьбы со спамом в MODX Revo с форм на FormIt, AjaxForm, FetchIt без капч.
Один из самых действенных методов борьбы со спамом — поставить Google reCAPTCHA. Данный метод тоже не без изъянов, останавливаться на нем не буду. Опишу альтернативные способы борьбы со спамом без использования надоедливых капч.
Способ 1: использование хуков FormIt (spam и math)
hooks spam
В форму добавляем скрытый импут: <input type="hidden" name="nospam" value="">
В вызове сниппета в hooks
добавляем spam
(обязательно перед хуком email), и в validate
добавляем nospam:blank
, пример:
...
&hooks=`spam,email`
&validate=`nospam:blank, name:required`
...
(hooks math)
В вызове сниппета в hooks
добавляем math
(обязательно первым — иначе не работает толком), и в validate
добавляем math:required
, пример:
...
&hooks=`math,spam,email`
&validate=`math:required, nospam:blank, name:required`
...
Далее в форму добавляем следующие поля:
<label>[[!+fi.op1]] [[!+fi.operator]] [[!+fi.op2]]?</label>
[[!+fi.error.math]]
<input type="text" name="math" value="[[!+fi.math]]">
<input type="hidden" name="op1" value="[[!+fi.op1]]">
<input type="hidden" name="op2" value="[[!+fi.op2]]">
<input type="hidden" name="operator" value="[[!+fi.operator]]">
Либо label
вообще лучше вывести вот так:
<label>[[!+fi.op1]] [[!+fi.operator:is=`-`:then=`минус`:else=`плюс`]] [[!+fi.op2]]?</label>
Способ 2: использование своих скрытых полей
Так как боты стали очень умными, нет смысла добавлять input type="hidden"
, тип импута должен быть нормальным: text или email.
Вариация 1.
Добавляем в форму строку <input type="text" class="s-message" name="s-message" value="">
, скрываем ее через css .s-message{display:none}
и добавляем в вызов, в параметр validate: &validate=`s-message:blank`
Вариация 2 (под AjaxForm).
Добавляем в форму строку <input type="text" class="n-message" name="n-message" value="">
, скрываем ее через более сложный css, например:
input[name="n-message"] {
display: block;
width: 5px;
height: 3px;
margin-bottom: -3px;
opacity: 0.01;
}
Далее создаем сниппет checkSpam:
<?php
if ($_POST['n-message']) { // проверяем наше поле на пустоту
echo $AjaxForm->success('Ваше сообщение отправлено');
die();
} else {
return true;
}
и добавляем его в качестве хука перед email:
'hooks' => 'checkSpam,email',
Теперь если письмо не отправлено, спамеру всё равно будет показано сообщение об успехе.
Вариация 3 (под AjaxForm).
За частую спам-боты заполняют все поля и отключат js, по этому добавляем с помощью js в форму поле, и если это поле будет заполнено или отсутствовать, форма не отправиться
Создаем сниппет validate со следующим содержимым:
<?php
function text_error(){
return false;
die();
}
if(isset($_POST['org'])){
if($hook->getValue('org')!=''){
$modx->log(xPDO::LOG_LEVEL_ERROR, 'Ошибка заполнения формы: не пустое поле антиспама');
$modx->log(xPDO::LOG_LEVEL_ERROR, print_r($_POST, 1));
text_error();
}else{
return true;
}
}else{
$modx->log(xPDO::LOG_LEVEL_ERROR, 'Ошибка заполнения формы: нет поля антиспама');
$modx->log(xPDO::LOG_LEVEL_ERROR, print_r($_POST, 1));
text_error();
}
В вызов формы хук validate — название выше созданного сниппета
&hooks=`validate, email, FormItSaveForm`
Ну и пишем небольшой скриптик на JavaScript (с jQuery)
$(function(){
// Антиспам
$('.ajax_form').append('<input type="text" name="org" value="" class="_org" style="visibility:hidden; height: 0; width: 0; padding: 0; border:none;"/>')
// Антиспам х
})
где .ajax_form — класс формы
Способ 3 — в основном от ручного спама ну и от части роботов
Суть в том что обычно спамеры пытаются отправить ссылку на какой-нибудь сайт, а реальные пользователи обычно просто задают вопрос, типа как сколько стоит, как купить, и т.д. Поэтому первое — запрещаем ввод ссылок. Так же в сниппете предусмотрена проверка поля ввода телефона.
Создаем сниппет antispam со следующим содержимым
<?php
/*Сниппет, проверяющий содержание формы на признаки спама*/
//Определяем значения служебных переменных по умолчанию
$success=true;
$haserror=false;
//Массив запрещенных фрагментов строк
//Если в каком либо поле формы встретится один из ниже перечисленных фрагментов, то далее обрабатывать сообщение не будем
$forb=array(
'@',
'http',
'https',
'://',
'www'
);
//Пробегаем по массиву запрещенных фрагментов
foreach ($forb as $f){
if($haserror==false){ //Если еще не встретился запрещенный фрагмент
$haserror=strpos($value,$f); //Проверяем его в значении текущего проверяемого поля ввода
}
}
/*Проверка телефона (если не нужна, просто закомментируйте строки 23-26)*/
if(substr_count($key,'phone')>0){ //Если проверке подвергается поле ввода телефона
if($value!='' and strlen($value)<16){$haserror=1;} //для поля ввода телефона проверяем длину введенного значения (при использовании скрипта форматирования это будет строка из 16 символов всегда!)
}
//Если была обнаружена ошибка
if ($haserror!=false) {
$validator->addError($key,'Недопустимое значение!');
$modx->log(xPDO::LOG_LEVEL_ERROR,'Антиспам: IP='.$_SERVER['REMOTE_ADDR'].' СОДЕРЖАНИЕ '.$key.': '.$value);
$success=false;
}
//Возвращаем результат работы валидатора
return $success;
Далее подключаем сниппет в formIt в виде кастомного валидатора
&customValidators=`antispam`
и прописывать валидацию к нужным полям. Как правило, это поле message.
&validate=`message:antispam`
Данный сниппет пишет содержание в лог на всякий случай.
Способ 4 — генерация ключа (для AjaxForm)
Создаем hidden-поле и записываем в него сгенерированный на сервере ключ, который кладем в сессию. Когда аякс с формой уйдет на сервер, происходит сверка значения из hidden-поля с тем, что лежит в сессии. В случае, если значения не равны — это спам, следовательно останавливаем отправку формы на почту.
<input type="hidden" name="message-key" value="[[!getMessageKey]]" />
где сниппет со следующим содержимым
if (!isset($_SESSION['spamProtectionMessageKey']) || empty($_SESSION['spamProtectionMessageKey'])){
$_SESSION['spamProtectionMessageKey'] = time();
}
return $_SESSION['spamProtectionMessageKey'];
И создаем еще один снимппет - хук: chekSpamProtectionMessageKey, со следующим содержимым:
$messageKey = $hook->getValue('message-key');
if (!isset($_SESSION['spamProtectionMessageKey']) || empty($_SESSION['spamProtectionMessageKey'])
|| empty($messageKey) || (int)$messageKey !== (int)$_SESSION['spamProtectionMessageKey']){
return false;
}
return true;
и добавляем вы вызов данный хук
&hooks=`spam,chekSpamProtectionMessageKey,email`
Способ 5 — ограничение на количество вводимых символов
В последнее время начали спамить практически в любые текстовые поля, например в поле имя (name) и для таких полей проще всего поставить ограничение на количество вводимых символов, например имя явно не может быть длиннее 15 символов, поэтому можно ограничить его так:
в параметр validate указать значение maxLength=^15^ — проверка, чтобы поле содержало не более 15 символов.
&validate=`name:maxLength=^15^`
Способ 6 -генерацией случайной строки
Вариация 1
- Создаём сниппет randString
<?php $permitted_chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; $output = 'antispam-'.substr(str_shuffle($permitted_chars), 0, 16); return $output;
- Добавляем в вызов формы, в параметр &validate
nobot:contains=^[[randString]]^
- Добавляем класс ajax_form форме.
- Добавляем скрипт внизу сайта (обычно у меня это чанк [[$scripts]])
<script> $(function(){ $('.ajax_form').append('<input hidden name="nobot" value="[[randString]]">') }); </script>
Вариация 2 (на fenom)
Все тоже самое, но еще и полю nobot (эта рандомная строка) присвоится только(!) при наведении на форму (а не как в вариации 1).
{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()
Прочие способы
Немного полазив по гуглу, нашёл еще пару способов: 1, 2, 2 — собирать ip и блочить спамеров, установить пакеты: Akismet, SpamProtect (платный), CSRFHelper — по большому счету тоже самое что генерация случайной строчки, только в виде отдельного пакета, который далее разберем.
Что помогло пишите в сообщениях) А если еще способы, делитесь.
Все эти способы работают и на FetchIt? Просто не вижу пометки с ним.
Способ 4 — под вопросом (не испытывал), остальные работают
I must thank you for the efforts you’ve put in writing this blog.
I’m hoping to check out the same high-grade blog posts from you in the future as well.
In fact, your creative writing abilities has motivated me to get my own, personal blog now 😉
Congratulations, good luck with your blogging)
Не сталкивался с тем, что сниппет validate приводит к 500 ошибке после отправки сообщения? PHP 7.3 Если отключаю проверку validate в hook то все работает
Давно им не пользуюсь (на modx 2 работал норм, когда писал), пользуюсь в основном методами 2, 5 и защита от CRF https://web-revenue.ru/modx-revo/csrfhelper
А как сделать в 3 способе, чтобы спаммер думал, что форма успешно отправлена, но по факту чтобы антиспам срабатывал ?
Попробуйте заменить $success=false; на echo $AjaxForm->success(‘Ваше сообщение отправлено’);
die();
Спасибо за статью, а как сделать так, чтобы если пользователь ввёл определённое значение (например номер телефона определённый) то форма бы не отправилась, а ему пришло уведомление об успешной отправке ?
В конце 3-го способа — лишний текст из конца 2-го дублируется
Поправил)
Третий способ полностью блокирует все отправления. Видимо что-то с хуком, т.к. getMsKey сниппет отрабатывает и генерит число.
Поправил 3 способ в статье, теперь должно работать