Защищаем веб-формы от спама без CAPTCHA — 2: Ботобор
Три года назад на Хабре была опубликована статья «Form Spam Bot Blocker: Защищаем Web-формы без CAPTCHA!», рассказывающая о принципиально отличном от CAPTCHA решении для PHP по защите форм от спам-ботов. Это решение основано на идеях, изложенных в своих статьях Филом Хааком (Phil Haack) — Honeypot Captcha и Недом Батчелдером (Ned Batchelder) — Stopping spambots with hashes and honeypots. К сожалению, предложенный в статье класс, написан для PHP4 и не развивается с 2007-го года. Хочу предложить вашему вниманию его аналог на PHP5.
Ботобор
Ботобор — библиотека, написанная на PHP 5.0, предназначенная для защиты от заполнения веб-форм роботами. Используемые ей методы, незаметны для посетителей-людей.
Для выявления роботов Ботобор использует следующие проверки:
- несовпадение значения REFERER с URL, на котором расположена форма;
- слишком маленький промежуток между показом формы и её отправкой (настраивается);
- слишком большой промежуток между показом формы и её отправкой (настраивается);
- заполнение поля-приманки.
По умолчанию используются все проверки, но у разработчика есть возможность отключать любые из них.
Примеры
Простой пример
require ‘botobor.php’ ;
…
// Получите разметку формы тем способом, который предусмотрен у вас в проекте, например:
$html = $form -> getHTML ( ) ;
// Создайте объект-обёртку:
$bform = new Botobor_Form ( $html ) ;
// Получите новую разметку формы
$html = $bform -> getCode ( ) ;
Фрагмент кода, обрабатывающего данные формы:
require ‘botobor.php’ ;
…
if ( Botobor_Keeper :: isHuman ( ) )
<
// Форма отправлена человеком, можно обрабатывать её.
>
Пример настройки формы
// пусть $html содержит код формы
$bform = new Botobor_Form ( $html ) ;
// отключаем поля-приманки
$bform -> setCheck ( ‘honeypots’ , false ) ;
// устанавливаем нижний предел заполнения формы в 2 секунды
$bform -> setDelay ( 2 ) ;
// устанавливаем верхний предел заполнения формы в 60 минут
$bform -> setLifetime ( 60 ) ;
$html = $bform -> getCode ( ) ;
В остальном всё также как и в первом примере.
Что у ней внутре?
Что делает Ботбор с кодом формы
В конструкторе Botobor_Form принимает HTML-код формы. В этот код, после открывающего тега , добавляется скрытый (display: none) , содержащий input[type=hidden] с мета-данными формы. Эти мета-данные хранят подписанную информацию о времени создания формы, установленных опциях и т. д. В этот же скрытый блок Ботобор может вставлять поля-приманки.
Поля-приманки
Поля-приманки предназначены для отлова роботов-пауков, которые находят формы самостоятельно. Такие роботы, как правило, ищут в форме знакомые поля (например, name) и заполняют их. Ботобор может добавить в форму скрытые от человека (при помощи CSS) поля с такими именами. Человек оставит эти поля пустыми (т. к. просто не увидит), а робот заполнит и тем самым выдаст себя.
По умолчанию в коде формы ищутся поля с любым из следующих имён: «name», «mail», «email» (список настраивается). У каждого найденного поля имя меняется на сгенерированную случайным образом комбинацию символов и создаётся скрытое средствами CSS поле с оригинальным именем.
Обратное преобразование имён будет сделано во время вызова метода Botobor_Keeper::handleRequest() или Botobor_Keeper::isHuman().
Буду рад, если кому-то пригодится.
Защита от спама PHP формы отправки данных
Реклама — двигатель прогресса. Рекламодатели постоянно ищут все новые способы рекламы своих товаров и услуг. Поэтому не исключено, что через вашу форму будут рассылать СПАМ — так называемый вид рекламной рассылки.
Как защитить форму от отправки таких данных. 100% защиты не может быть, так как никто никогда не запрещает заполнить вашу форму вручную. Но все же мы можем защитить ее от роботов (программ), которые могут отправлять сотни сообщений в секунду.
Создание скрытого поля с шифром при помощи PHP
Простейший способ – создание скрытого поля с генерацией в нем набора спец символов.
Прежде всего, мы добавляем в форму скрытое поле.
То есть в это поле подставляется переменная $zaschita, которую мы должны сгенерировать заранее. Это сделаем следующим образом:
Мы шифруем время запуска формы и имя ivan. Получается, что каждую секунду данная переменная будет изменена, а алгоритм шифрования известен только вам. Вы можете использовать любой другой метод шифрования или использовать набор любых символов вместо ivan. Для того, чтобы обойти такую защиту в вашей форме потребуется разгадать метод шифрования. Поверьте – это возможно, но на это потребуется некоторое время. И, если это произошло, и защита PHP формы отправки данных на почту не работает, то вы всегда можете поменять шифр.
Проверка передаваемого шифра на PHP
Со стороны PHP мы должны принять скрытую переменную и проверить ее тем же образом, но при этом нам обязательно нужно создать новую переменную и зафиксировать в ней время загрузки формы, иначе наша простейшая защита PHP формы от спама будет работать против нас и не даст пользователю отправить сообщение. Для этого создаем переменную, также шифруя ее:
Затем прикрепляем переменную при помощи GET массива.
После этого нам нужно при отправке формы сформировать шифр и сравнить его с тем, который передан нам в скрытом поле.
$pol_time = $_GET['p']; $vernii_shifr = md5($pol_time."ivan"); $pol_shifr = $_POST['zf'];
Сравниваем их. Если совпадают, то отправляем данные на почту, иначе выводим сообщение:
If($vernii_shifr == $pol_shifr) < …. код отправки письма …. >else
Таким образом, полный код PHP формы с защитой от спама выглядит следующим образом:
" method="post"> "> else < $pol_time = $_GET['p']; $vernii_shifr = md5($pol_time."ivan"); $pol_shifr = $_POST['zf']; If($vernii_shifr == $pol_shifr)< $fio = $_POST['fio']; $email = $_POST['email']; $fio = htmlspecialchars($fio); $email = htmlspecialchars($email); $fio = urldecode($fio); $email = urldecode($email); $fio = trim($fio); $email = trim($email); if (mail("to@email.ru", "Заявка с сайта", "ФИО:".$fio.". E-mail: ".$email ,"From: ot@email.ru \r\n"))< echo "Сообщение успешно отправлено"; >else < echo "При отправке сообщения возникли ошибки"; >> else < echo "Не нужно здесь спамить"; >> ?>
Попробуйте посмотреть исходный код данного скрипта в браузере и обновляйте его каждую секунду – вы увидите, что ключи постоянно меняются.
Теперь предлагаю усложнить защиту, т.е. добавить головной боли для спамеров.
Для этого можно создать динамическую переменную в скрытом поле:
$t1 = substr(time(), 9, 1); $t2 = substr(time(), 7, 1); $t3 = substr(time(), 8, 1); $t_vse = $t1+$t2+$t3;
Например текущее время «1547380408». При помощи скрипта мы берем 10-й, 7-й и 8-й символы и просто их складываем, получая переменную $t_vse.
Далее нужно передать еще одним скрытым полем текущее время,вместо GET запроса, предварительно зашифровав его с возможностью расшифровки:
$kluch = '3287987984'; $shifruem = time(); $kript = mcrypt_create_iv( mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC), MCRYPT_DEV_URANDOM ); $shifr = base64_encode( $kript . mcrypt_encrypt( MCRYPT_RIJNDAEL_128, hash('sha256', $kluch, true), $shifruem, MCRYPT_MODE_CBC, $kript ) );
$rsh = $_POST['d']; $dannie = base64_decode($rsh); $kript = substr($dannie, 0, mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC)); $nashe_vremya = rtrim( mcrypt_decrypt( MCRYPT_RIJNDAEL_128, hash('sha256', $kluch, true), substr($dannie, mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC)), MCRYPT_MODE_CBC, $kript ), "\0" ); $vernii_shifr = md5(md5($nashe_vremya)."ivan"); $t1 = substr($nashe_vremya, 9, 1); $t2 = substr($nashe_vremya, 7, 1); $t3 = substr($nashe_vremya, 8, 1); $t_vse = $t1+$t2+$t3; $z = 'z_'.$t_vse; $pol_shifr = $_POST[$z];
Здесь мы воссоздаем, расшифровывая данные, переменную z_<число>, затем получаем наш первый шифр и сверяем его. Таим образом защита от спама PHP формы была усилена и позволит не использовать поля для ввода символов и т.п., что отпугивает пользователей.
Полный исходный код нашей PHP формы отправки данных с защитой от спама выглядит следующим образом:
число>
" value=""> "> else < $rsh = $_POST['d']; $dannie = base64_decode($rsh); $kript = substr($dannie, 0, mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC)); $nashe_vremya = rtrim( mcrypt_decrypt( MCRYPT_RIJNDAEL_128, hash('sha256', $kluch, true), substr($dannie, mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC)), MCRYPT_MODE_CBC, $kript ), "\0" ); $vernii_shifr = md5(md5($nashe_vremya)."ivan"); $t1 = substr($nashe_vremya, 9, 1); $t2 = substr($nashe_vremya, 7, 1); $t3 = substr($nashe_vremya, 8, 1); $t_vse = $t1+$t2+$t3; $z = 'z_'.$t_vse; $pol_shifr = $_POST[$z]; if($vernii_shifr == $pol_shifr)< $fio = $_POST['fio']; $email = $_POST['email']; $fio = htmlspecialchars($fio); $email = htmlspecialchars($email); $fio = urldecode($fio); $email = urldecode($email); $fio = trim($fio); $email = trim($email); if (mail("to@email.ru", "Заявка с сайта", "ФИО:".$fio.". E-mail: ".$email ,"From: ot@email.ru \r\n"))< echo "Сообщение успешно отправлено"; >else < echo "При отправке сообщения возникли ошибки"; >> else < echo "Не нужно здесь спамить"; >> ?>
Естественно эту защиту можно обойти, если разгадать данный метод, но на это потребуется немало сил и времени.
На рисунке я представил исходный код в браузере, обратите внимание на два скрытых поля. В первом каждую секунду меняется и шифр и название переменной, во втором просто меняется шифр.
На следующем рисунке отображен исходный код через несколько секунд.
Вы всегда можете использовать Google ReCaptcha для защиты от спама. Это более надежная защита.
А по поводу защиты PHP формы от спама, описанной в данной статье, я предлагаю в комментариях описать возможные способы ее обхода для того, чтобы мы могли ее усложнить и сделать более надежной, при этом не перегружая пользователя лишними и раздражающими действиями. Пока главная проблема здесь в том, что все ключи видны пользователю. Можно хранить закрытые ключи в базе данных или банально в файле и это усложнит задачу взлома для любителей СПАМА.
Ставим надёжную защиту от спама на сайте своими силами
Замучил спам? Я написал скрипт, который защитит Ваc от ненужных писем через формы обратной связи Вашего сайта.
Раньше я пользовался reCAPTCHA от Google . Но после того, как посмотрел на его исходный код, а в частности его вес (314 Кб в несжатом виде для версии v3), а также количество инициируемых им запросов, я отказался от этой идеи и написал всё сам. Работа скрипта проверена на многих сайтах, никакого спама не прошло.
В целях безопасного сохранения всех заявок через формы связи предусмотрена запись логов.
Ниже привожу php скрипт, с подробными комментариями.
Для начала делаем форму. Среди полей формы вставляем скрытый input с логичным для понимания атрибутом name. Представим что наша форма следующего вида: