Зашёл намедни на свой форум. Обнаружил под две сотни спамеров. Ужаснулся. Задумался. Почитал кучу материалов по способам защиты phpBB, в том числе и здесь. Не нашёл для себя подходящего. Изобрёл свой способ защиты форм от спам-ботов. Сразу говорю, на уникальность не претендует, ибо возможно уже существует, только я об этом не знаю. Панацеей также не является.
Большая часть форм защищается от повторной отправки и примитивных автоматических запросов так называемыми токенами. Суть токена в том, что при формировании формы в сессию записывается некая переменная, содержащая строку случайных символов. При отправке формы проверяется скрытое поле формы, содержащее строку, записанную в сессию. По сути, пока мы не получим HTML-код формы, мы не узнаем этот самый токен. Это защищает скрипт, обрабатывающий форму от автоматического запроса. А при обработке формы переменная токена сбрасывается или меняется. Таким образом при повторной отправке формы токен будет уже недействителен.
Современные спам-утилиты давно уже работают на принципе парсинга HTML-кода форм. Запрашивается форма, парсится её HTML-код, выдёргиваются все токены и т.п… В общем токен нынче может защитить разве что от глюков самих форм. А настроить спам-утилиту под конкретный движок, например phpBB — не проблема. Достаточно указать, какие поля и чем именно нужно заполнять. Да, не спорю, можно накрутить дополнительных полей в форму. Это немного собъёт с толку спамеров. Но ненадолго. Пройдёт некоторое время и спамеры перенастроят свои утилиты конкретно под Ваш форум и с ещё большим остервенением будут гадить.
При помощи механизма сессий можно заставить спамеров попотеть. На принципе токена можно создать поле у которого меняется не значение, а… имя. Притом лучше всего вместо дополнительного поля поменять таким образом стандартное поле, скажем, «имя пользователя». Итак, для начала вводим переменную сессии. Назовём её username_field_id. В этой переменной мы будем хранить ID поля «имя пользователя». А попутно оставим прежний INPUT в качестве поля-ловушки и скроем его с глаз долой. Полагаю, принцип ловушки-пустышки описывать не требуется.
В форме:
<?php
// После того, как стартовала сессия и сделаны все необходимые дела, генерируем рандомную строку...
$nfi = '';
for ($c = 0; $c < 16; $c++) {
$i = rand(0,61);
$nfi.= substr('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789',$i,1);
}
// ... и записываем её в сессию
$_SESSION['username_field_id'] = 'field'.$nfi;
?>
<input id="username" name="username" type="text" value="" />
<input id="<?php echo $_SESSION['name_field_id']; ?>" name="<?php echo $_SESSION['name_field_id']; ?>" type="text" value="" />
В обработчике:
// После того, как стартовала сессия и сделаны все необходимые дела...
// ... проверяем ловушку-пустышку...
if ($_POST['username'] != '') die('Get the fuck away from my forum, spammer!');
// ... и наше ниндзя-поле
$nfi = $_SESSION['username_field_id'];
if (isset($_POST[$nfi])) {
if ($_POST[$nfi] != '') {
// Делаем нужные манипуляции с полем-м-м.
} else {
// Сообщаем юзеру, что поле имя не заполнено
}
} else { die('Get the fuck away from my forum, spammer!'); }
Важно при этом понимать, что атрибут VALUE должен быть принудительно установлен в пустую строку. Иначе есть риск проколоться.
Однако, более продвинутые спам-утилиты могут определить, что наше ниндзя-поле должно содержать, да ещё и ловушку-пустышку не заполнить. Нет таких утилит? Ничего страшного, подумаем на пару шагов вперёд. Поиграем со спамером в «напёрстки» (кто не знает, что это, гуглим сами). Мы создадим по такому же принципу пару дополнительных ловушек-пустышек. Но не просто создадим их, но и перемешаем в рандомном порядке вместе с настоящим полем.
В форме:
<?php
// Упакуем алгоритм генерации в функцию для удобства
function gen_random_string(){
$nfi = '';
for ($c = 0; $c < 16; $c++) {
$i = rand(0,61);
$nfi.= substr('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789',$i,1);
}
return $nfi;
}
// После того, как стартовала сессия и сделаны все необходимые дела, генерируем три рандомных поля
$_SESSION['unf_1'] = 'field'.gen_random_string();
$_SESSION['unf_2'] = 'field'.gen_random_string();
$_SESSION['unf_3'] = 'field'.gen_random_string();
// Генерируем, какое из этих полей будет настоящим
$_SESSION['unf_t'] = rand(1,3);
?>
<input id="username" name="username" type="text" value="" />
<?php
for ($c = 1; $c < 4; $c++) {
echo '<input id="'.$_SESSION['unf_'.$c].'" name="'.$_SESSION['unf_'.$c].'" type="text" value="" />';
}
?>
В обработчике:
// После того, как стартовала сессия и сделаны все необходимые дела получаем номер настоящего поля
$true_field = $_SESSION['unf_t'];
// ... проверяем ловушки-пустышки...
if (isset($_POST['username'])) if ($_POST['username'] != '') die('Get the fuck away from my forum, spammer!');
for($c = 1; $c < 4; $c++) if ($c != $true_field) {
$fn = $_SESSION['unf_'.$c];
if (isset($_POST[$fn])) if ($_POST[$fn] != '') die('Get the fuck away from my forum, spammer!');
}
// ... и наше ниндзя-поле
$nfi = $_SESSION['unf_'.$true_field];
if (isset($_POST[$nfi])) {
if ($_POST[$nfi] != '') {
// Делаем нужные манипуляции с полем-м-м.
} else {
// Сообщаем юзеру, что поле имя не заполнено
}
} else { die('Get the fuck away from my forum, spammer!'); }
При этом нам нужно скрыть поля пустышки, оставив только настоящее поле. Делать будем посредством внешней таблицы стилей. Подключать к основному стилю её мы будем через директиву import.
В основном стиле:
@import 'generator.php';
PHP-генератор таблицы стилей generator.php:
<?php
// Устанавливаем нужный заголовок
header("Content-type: text/css;");
// Всеми правдами-неправдами запрещаем кэширование
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); //Дата в прошлом
header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1
header("Pragma: no-cache"); // HTTP/1.1
header("Last-Modified: ".gmdate("D, d M Y H:i:s")."GMT");
// Получаем истинное поле
session_start();
$n = $_SESSION['unf_t'];
// Скрываем все поля, кроме истинного
for($c = 1; $c < 4; $c++) if ($n != $c) echo '#'.$_SESSION['unf_'.$c].' { display: none; }';
?>
Таким образом спам-утилита столкнувшись с такой формой, не сможет определить какое из трёх рандомных полей необходимо заполнить. Не спорю, есть спам-утилиты, могущие по стилям определить, какое поле видимо. Но таких утилит, слава Роду, пока немного.
Изначально данное решение писалось в расчёте на конкретный движок — phpBB. Однако, разобраться, как интегрировать данное решение в движок, я пока не смог. Как разберусь, напишу ещё.
Автор: XanderBass