Очередная анти-спам проверка без каптчи (обновлено)

Меня лично дико раздражает каптча, в особенности, когда великая и могучая гугля шесть раз подряд пытается у меня узнать как выглядят витрины и дороги, а если нужно вводить циферки написанные на тарабарском то вообще труба. Это меня как пользователя она раздражает. А как программиста — ее не всегда корректно впишешь в дизайн. Поэтому я искал решения, которые бы отсеивали спам, но при этом не использовали каптчу.

По сути было найдено два решения:

1) Скрытое поле и проверка на пустоту, но, к сожалению, роботы научились распознавать такие поля.
2) использовать сервис Akmist, который проверяет содержимое, но почему-то он отвалился и я так не сумел найти к нему подход.

В итоге — написал простое, но тие не менее эффективное решение, основанное на проверке проведенного времени на странице. Роботы они ведь что делают? Загружают страницу, парсят, находят форму, заполняют — отправляют. На все про все по моей личной статистике уходит не больше секунды, в то время как обычный пользователь для заполнения формы тратит как минимум секунд 30.

Итак, от слов к делу.

Для eForm (evolutionCMS).

Создаем сниппет checkSpamTime, вставляем в него код:

<?php
if ($_SESSION['now'])
{
	$_SESSION['lt'] = $_SESSION['now'];	
}
$_SESSION['now'] = microtime(true);

if (!function_exists('checkSpamTime')) 
{
	function checkSpamTime(&$fields)	
	{			
		$time = $_SESSION['now'] - $_SESSION['lt'];		
		if ($time<2) exit(); //Здесь проверяем сколько секунд прошло с момента точки входа. 5 - с запасом, можно ставить и единицу
	}
}


Далее делаем вызов сниппета над вызовом eForm (если используется несколько форм, необходимо поставить ОДИН раз над самой верхней), и в самом сниппете пишем &eFormOnBeforeMailSent=`checkSpamTime`

В итоге у нас получается что-то вроде такого:

[!checkSpamTime!]
[!eForm? 
/*...*/
&eFormOnBeforeMailSent=`checkSpamTime`
/*...*/
!]


Для formLister (evolutionCMS)

Создаем сниппет checkSpamTimeFL, вставляем в него код

<?php
if ($FormLister->isSubmitted())
{
    $flag = false;
    $now = microtime(true);
	
    if ((isset($_SESSION['now'])) && (($now - $_SESSION['now']) > 2)) 
    {		
        $flag = true;
    }
    $FormLister->setValid($flag);
}
else 
{   
	$_SESSION['now'] = microtime(true);
}


и вызываем его в prepare.
В итоге у нас получается что-то вроде такого:
[!FormLister? 
/*...*/
&prepare=`checkSpamTimeFL`
/*...*/
!]


За обновление решения спасибо Pathologic

Для FormIt (Modx Revolution)

Создаем сниппет checkSpamTime, вставляем код:
<?php
if (count($_POST))
{
    $flag = false;
    $now = microtime(true);
  
    if ((isset($_SESSION['now'])) && (($now - $_SESSION['now']) > 2)) 
    {	
        $flag = true;
    }
    return $flag;
}
else 
{   
	$_SESSION['now'] = microtime(true);
}

Замечание. Я с Ревой знаком поверхностно, поэтому может кто подскажет: на что заменить проверку count($_POST)? Я че-та не нашел ничего более изящного((

И вписываем его первым в хук и пре хук, т.е.
[[!FormIt? 
/*...*/
&preHooks=`checkSpamTime`
&hooks=`checkSpamTime,email`
/*...*/
!]]


Все три варианта проверенны — работают)

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

avatar
обычный пользователь для заполнения формы тратит как минимум секунд 30
А пользователь с KeePass примерно одну секунду при входе на сайт для ввода логина и пароля.
Комментарий отредактирован 2018-02-19 09:06:20 пользователем Extremum
avatar
Тебе нужна каптча при вводе логина и пароля?
avatar
В личном кабинете бывает нужна.
avatar
Тогда используешь другие решения. Например трехразовую проверку неправильного ввода и т.п. Здесь все-таки речь идет об отправке писем и проверке на СПАМ.
avatar
То что я использую — это мое дело, я просто дал кейс при котором утверждение про определенное время на сайте не является верным. Я сюда и форму заказать телефон могу отнести, тоже пару секунд уходит на ввод. Зайдет новичок, поставит себе это решение, а потом будет создавать топики — «а че не работает». Тут же не написано что эта капча работает только с формами где требуется много данных вводить. И это у меня еще автозаполнение выключено, там за три секунды можно полей 5 вбить.
avatar
И кстати, если бот ждет полной загрузки страницы, а какой-нить js будет грузиться 5 секунд, то тоже не сработает.
avatar
Новичок скорее в обычной каптче сможет накосячить) А для того, чтобы вбить поля за 3 секунды, нужно еще чтобы страница прогрузилась, долистать до формы, понять что тебе ее нужно вводить и т.д. Плюс к этому — время в коде специально указано. Я тебя услышал, но твои замечания не принимаю.
avatar
Да ради бога, никто никого не заставляет ничего принимать. И тыкать незнакомым людям как-то не принято, но это уже дело воспитания каждого.
avatar
Для FormLister достаточно одного сниппета в prepare:

if ($FormLister->isSubmitted()) {
    $flag = false;
    $now = microtime(true);
    if (isset($_SESSIOON['now'] && ($now - $_SESSION['now'] > 5)) {
        $flag = true;
    }
    $FormLister->setValid($flag);
} else {
    if (!isset($_SESSION['now'])) {
        $_SESSION['now'] = microtime(true);
    }
}


А session_start() вообще не нужно вызывать в сниппетах.
avatar
Ок, спасибо, сейчас поправлю
avatar
Там пара опечаток у меня, ну и вызывать этот сниппет нужно именно в prepare, prepareProcess не годится.
avatar
Те которые заметил — поправил) На счет prepare понял сразу) Не знал про $FormLister->isSubmitted()… Надо чаще читать документацию)))
avatar
А этого там нема)
avatar
imho.
если робот надет форму — то будет ее долбить. В худшем варианте начнет долбить часто и со многих IP (в самом худшем случае).
если сразу не найдет (форма погружается) аяксом — никакой потенциально возможной нагрузки в подобном случае не будет.

а поскольку вызов показа формы можно запрограммировать как угодно (трудно читаемо), то шансов, что роботы научатся находить аякс формы пока что ничтожно мал. Например «пустое» поле научились обходить где-то за пол-года с момента предложенного @dmi3yy.
  • swed
  • 0
avatar
По поводу времени пребывания. Мало вероятно конечно, но если пользователь воспользуется автозаполнением?..
Возможно стоит дополнительно использовать JS временное ограничение на отправку.

и… замерим пол годика, что бы посмотреть как роботы научатся задерживать дыхание :)
  • swed
  • +1
avatar
уже умеют. Достаточно добавить в скрипт
$FormLister->setField('session',$now - $_SESSION['now']);

чтобы в письме получать время сессии.
avatar
Прошло больше чем полгодика. Решение только с моей стороны работает на десятках проектах. Жалоб пока не поступало.
avatar
Привет.
В FormLister вызывает вот такую ошибку:


Fatal error: Call to a member function isSubmitted() on null in ***/httpd.www/manager/includes/document.parser.class.inc.php(1926) : eval()'d code on line 1


После обновления страницы уже показывает ошибку 500.
avatar
а у меня работает, только что проверил
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.