Здравствуйте уважаемые читатели! Мой блог стали часто находить по запросам связанным с формой обратной связи, а ещё я стал оставлять ссылки на тематических форумах людям, которые просили подсказать им хорошую форму обратного звонка для их сайта. Так вот эти события заставили меня отложить все свои дела и написать обещанную статью о том, как же сделать свой собственный анти-спам для этой (или любой другой) формы обратной связи.
Как обычно, созданный в этой статье скрипт полностью универсален и будет работать для любого сайта с любой CMS.
Сразу скажу, что я не искушенный практик в этой области, но люблю придумать какой-нибудь новый (пусть и простой) алгоритм для защиты от случайных спам-ботов.
По сути, мы можем защищаться от спама на двух уровнях: серверном (PHP) и клиентском (JavaScript). Ярким примером защиты от спама на PHP может послужить пресловутая капча.
Алгоритм таков: мы с помощью PHP рисуем картинку с буквами и цифрами (да, PHP умеет рисовать не хуже Paint), запоминаем эти символы, например, по средствам cookie, выводим их пользователю на экран и просим его ввести слово с картинки, после чего он их вводит и жмёт кнопку "отправить". Нам приходят эти цифры, мы сверяем пришедшие цифры со значением в cookies пользователя и если они равны, то выполняем нужное пользователю действие, например, публикуем комментарий.
Как сделать капчу на PHP своими руками я расскажу в другой статье, а сейчас я хочу выделить некоторые недостатки этого метода:
Для того чтобы решить эти две проблемы мы можем использовать другой вид анти-спама, построенный чисто на JavaScript. То есть мы не дадим посетителю отправить форму, пока он не выполнит какое-то действие в своём браузере, например, перетянет квадратик из одного угла в другой или пока не ответит на вопрос.
Минус у подобных способов один: обойти яваскрипт ещё легче, чем обойти проверки на PHP. Но под словом «обойти» я подразумеваю хакера, который целенаправленно будет писать скрипт конкретно под ваш сайт.
Не будем углубляться в эту тему дальше, наша задача защитить свой сайт от случайных спам-ботов, которые заходят на все попавшиеся сайты с целью оставить бессмысленный спам (информационный шум) или рекламу.
Также хочется отметить, что способ, описанный в этой статье дальше, естественно, уже зарекомендовал себя. Я вам "лишь бы что-то" никогда не советую :)
Этот простой алгоритм и не менее простой скрипт я придумал для одного из своих интернет-магазинов на Joomla 2.5 + VirtueMart 2. Мне постоянно приходил спам из формы "Задайте вопрос об этом товаре", после чего я залез в отвечающий за неё файл (/components/com_virtuemart/views/askquestion/tmpl/form.php) и установил там свой анти-спам: проблема была решена!
До тех пор пока пользователь не введёт ответ, мы не отрисуем ему кнопку "отправить":
Пользователь вводит правильный ответ и мы на лету создаём для него кнопку:
Также работу скрипта вы можете увидеть в статье, где я рассказывал как сделать форму обратной связи, только там есть небольшое ограничение по функционалу: вопрос всегда один и тот же. Почему? Потому что даже одного вопроса хватает для защиты от спама, но для своих читателей я подготовил более сложный и интересный вариант.
Перейдём к созданию. Для того чтобы создать свой собственный анти-спам на Яваскрипте + немного PHP (для динамики) нам потребуется форма обратной связи (подопытный кролик), знание о многомерных массивах в PHP и функция, которую мы использовали для ротации баннеров на сайте – shuffle().
То есть из всего вышеперечисленного, новым для моих постоянных читателей окажется только сам код JS. Но давайте обо всём по порядку: первым делом нам нужно создать многомерный массив на PHP, в который мы будем заносить вопросы и ответы для нашего анти-спама, после чего мы будем перемешивать массив и выводить случайный вопрос:
<?php
$spam[] = array("Три плюс семь равно...","v.match(/^десять$/i)||v=='10'");
$spam[] = array("Пять минус пять равно...","v.match(/^ноль$|^нуль$/i)||v=='0'");
$spam[] = array("Шесть плюс один равно...","v.match(/^семь$/i)||v=='7'");
shuffle($spam); /* Перемешиваем массив */
?>
Выше я создал три вопроса, у вас их может быть сколько угодно, лично мне хватает и одного. Разберёмся более подробно:
$spam[] = array("Три плюс семь равно...","v.match(/^десять$/i)||v=='10'");
Итак в круглых скобочках мы указываем 2 ячейки: первая (до запятой) это наш вопрос, а вторая – ответ. В нашем примере в ответ мы записали следующее:
v.match(/^десять$/i)||v=='10'
Мы перечислили два возможных варианта ответа, первый варианта (до ||):
v.match(/^десять$/i)
В нём мы указали регулярное выражение, а именно: ответ должен содержать слово "десять" и ничего кроме этого, а флаг i после слеша обозначает, что регистр не имеет значения. Тоже самое можно было записать и так:
v=='десять'
Но тогда бы регистр имел значение, и слово "Десять", написанное с большой буквы оказалось бы не верным.
Количество ответов может быть каким угодно, вот пример, когда правильных ответов четыре:
$spam[] = array("4 - 2 = ?","v=='два'||v=='две'||v=='двум'||v=='двумя'");
Если вам нужно указать несколько слов внутри регулярного выражения, то это, как я уже не раз писал в других статьях (например, в статье про дубли страниц), делается с помощью одинарной вертикальной черты:
$spam[] = array("5 - 5 = ?","v.match(/^ноль$|^нуль$/i)||v=='0'");
Пример выше можно переделать и так:
$spam[] = array("5 - 5 = ?","v.match(/^ноль$|^нуль$|^0$/i)");
В данном случае мы число 0 тоже занесли в регулярное выражение. Надеюсь смысл как записывать ответ, вам понятен, здесь нет абсолютно ничего сложного, особенно, если вы читали мои предыдущие статьи о регулярных выражениях.
Теперь вернёмся к форме обратной связи, чтобы не лить лишней воды, сразу напишу, как она у нас должны выглядеть теперь:
<div onclick="openn(this)" id="callback"></div><div id="body-form"> <form method="post" action="<?php echo $_SERVER['REQUEST_URI'] ?>"> Имя: <input required="required" maxlength="30" type="text" name="name" /><br /> Телефон: <input required="required" maxlength="30" type="text" name="phone" /><br /> Сообщение: <br /><textarea rows="7" cols="50" required="required" name="message">Пожалуйста, перезвоните на указанный номер.</textarea> <input type="hidden" name="check" value="fd13vv" /> <strong><?php echo $spam[0][0] ?></strong> Ответ: <input type="text" id="myspam" size="20"/> </form> </div>
В чём суть? Мы полностью удалили кнопку "отправить":
<input type="submit" value="Отправить!" />
Теперь она будет отрисовываться на лету с помощью Яваскрипта только после того, как пользователь правильно ответит на наш вопрос. Вместо кнопки "отправить" у нас появилось две новых строчки:
<strong><?php echo $spam[0][0] ?></strong> Ответ: <input type="text" id="myspam" size="20"/>
В первой мы отрисовываем случайный вопрос и заключаем его в тег strong, чтобы он выделялся полужирным. Во второй строчке мы указываем поле для ввода ответа на вопрос. Внимание! Не меняйте id у этого input, а если меняете, то не забудьте поменять его и в JS, вот, кстати, и он:
<script type='text/javascript'> document.getElementById('myspam').onkeyup = function(e){var v = this.value;if(<?php echo $spam[0][1] ?>){var inp = document.createElement('DIV');inp.innerHTML = '<input type="submit" value="Отправить!" />';this.parentNode.insertBefore(inp,this.nextSibling);this.onkeyup = null;}} </script>
Я специально записал его в одну строчку, чтобы ваши страницы загружались быстрее. В этом javaScript коде мы указываем id поля, в который пользователь должен ввести свой ответ, у нас это myspam. Далее перечисляем все правильные варианты ответов, которые должны прийти в переменную v и если они приходят, то отрисовываем кнопку "отправить".
Примечание:
JS в коде должен находиться ниже формы, например, сразу после неё. Лично я размещаю его в самом конце страницы, перед закрывающимся тегом body. При этом JS нельзя выносить во внешний файл, так как в нём присутствует PHP код.
Вот и всё, уважаемые читатели, без кнопки "отправить" ни один случайный спам-бот никогда не сможет отправить вам спам. Для человека-хакера, который захочет вам насолить, обойти этот анти-спам не должно составить особого труда, но это уже совсем другая история, так как 80% спама идёт через автоматические скрипты и мало кому станет интересно писать скрипт ради одного вашего сайта.
Обновление от 26.10.2013: практика показала, что если ваш сайт не на Joomla, а на голом PHP, то нужно обязательно добавить в PHP обработчик проверку на ответ (заполнено ли поле с ответом), например, с помощью функции !empty($_POST["имя поля с ответом"])
На сегодня всё, спасибо за внимание. Не забывайте подписаться на новые статьи блога, чтобы всегда быть в курсе полезных идей для вашего сайта, удачного дня и до встречи!
Пожалуйста, прокомментируйте, как Вам моя статья?