Дискриминация автопостинг-ботов по интервалу между запросами при помощи PHP
- Характерные особенности спама автопостингом
- Постановка задачи
- Готовый функциональный модуль
- Практические результаты
- Примеры практического применения
Характерные особенности спама автопостингом
Сначала предлагаю рассмотреть некоторые основные характерные черты «продвижения» сайтов методом автопостинга (спама) в комментарии и форумы. А затем уже начинать с этим явлением бороться в меру своих чайниковских паровых сил.
Во-первых, все пользуются специальными программными средствами,а попросту — ботами. Трудно себе представить, что кто-то двадцать четыре часа в сутки и семь дней в неделю ручками проходит по списку адресов и вбивает нужные строчки. Можно, конечно, нанять толпу гастарбайтеров и запереть их в подвале, но получится дороговато.
Во-вторых, боты маскируются под популярные браузеры (обычно — под Mozilla Firefox), прописывая нужные данные в поле запроса User-Agent.
В-третьих, подобной дурью занимаются, обычно, весьма недалёкие люди, которые еще верят в эффективность подобного метода. Их, как и в целом по земному шару, процентов 90.
Остальные 10% делают деньги на этой массе хомячков (вот вам и «заработок в сети»), впаривая им как сами программы для спама, так и базы адресов к ним. Я тоже там состою почётным членом в общей базе для популярной спамерской приблуды XRumer. А по нечётным — играюсь с этими милыми зверушками и их не менее забавными хозяевами.
Сами боты, несмотря на продвинутый искуственный интеллект, довольно-таки туповаты в плане освоения новых горизонтов. Под стать им и хозяева — получив в цепкие лапки вожделенный продукт они сразу запускают его в дело, не удосужившись даже приказать роботу вести себя по-человечески и cделать интервал между запросами поболее секунды. В результате имеем, что некто пришел, за пару сотен миллисекунд заполнил форму комментария и отправил данные (строки лога обрезаны):
[19/Feb/2011:21:27:59 +0300] "GET /first-record/ HTTP/1.0" 200 15232 [19/Feb/2011:21:27:59 +0300] "POST /first-record/ HTTP/1.0" 200 20008 [19/Feb/2011:21:27:59 +0300] "GET /first-record/ HTTP/1.0" 200 15232
Код «200» вместо редиректа «302» означает, что робот напоролся на другой защитный механизм и получил ошибку, например, о том, что URL, который он собрался запостить, забанен.
Хотя иногда, по ночам, наблюдаю и более длинные интервалы: 2-3 и даже 5-10 секунд. Это или «работают» зверушки вышеупомянутых 10% продвинутых спамеров, которым хватает ума покопаться в настройках питомца, или большие задержки ответа сервера из-за резервного копирования данных, или у хозяина автопостинг-бота проблемы со связью.
Постановка задачи
Задача даже для новичка довольно простая: требуется засечь время текущего запроса GET и при последующем POST проверить, сколько же там времени прошло. Если маловато, тогда выдать ответ сервера «403 доступ запрещен» и прекратить работу скрипта. А зачем создавать нагрузку и выполнять какие-то операции ради электронного болвана? Следовательно, проверку надо производить в начале php-скрипта, до запуска основной функциональной части.
Все новички, кто внезапно ощутил прилив сил, могут не смотреть вниз, а пойти и реализовать этот элементарный механизм своими силами. Кто не хочет — смотрите дальше, но я за возможные глюки вашего движка ответственности не несу.
Готовый функциональный модуль
Был написан примерно минут за 20, и то — потому что постоянно дергали и отвлекали. Реализовано сохранение времени последнего запроса (любого вида) в сессию ($_SESSION) и проверка временного интервала, прошедшего со времени последнего запроса. Функционал можно использовать по частям, так что подойдет практически для любых условий и предпочтений.
Сохраните код в php-файл и подключите (requre_once) перед вызовом функций.
// Ключ массива для сохранения времени запроса - // измените эту константу, на всякий случай. define ('ANTIBOT_SESSION_TIME','antibot_session_time'); // По-умолчанию, не меньше пяти секунд для запроса POST define ('ANTIBOT_TIME_LIMIT_POST', 5); // По-умолчанию, не меньше 200 миллисекунд для запроса GET. // Эта величина нуждается в тестировании на практике и "подгонке". define ('ANTIBOT_TIME_LIMIT', 0.2); // HTTP-заголовок "403 доступ запрещен". define ('ANTIBOT_HEADER', 'HTTP/1.1 403 Forbidden'); function antibot_save_time() {// Сохраняем время в сессию. $_SESSION[ANTIBOT_SESSION_TIME] = microtime(true); //Не забывать вызывать эту функцию при каждом //исполнении скрипта. } function antibot_check_time($limit=ANTIBOT_TIME_LIMIT) {// Проверка временного интервала между запросами. if (!isset($_SESSION[ANTIBOT_SESSION_TIME])) { // Если ещё не сохраняли время ни разу // значит, сравнивать пока не с чем . return true; } else { // А иначе - проверим и вернем результат return (microtime(true) - $_SESSION[ANTIBOT_SESSION_TIME]) > $limit; } } function antibot_is_post() {// Метод запроса "POST"? return $_SERVER['REQUEST_METHOD'] == 'POST'; } function antibot_check_post($limit=ANTIBOT_TIME_LIMIT_POST) {// Проверяем только POST запросы. if (!antibot_is_post()) return true; else return antibot_check_time($limit); } function antibot_header() {// Этот запрет пошлем боту. header(ANTIBOT_HEADER); echo '<h1>'.ANTIBOT_HEADER.'</h1>'; }
Практические результаты
До подключения вышеописанной мелочи, у меня на сайте работала только примитивная подсистемка, которая заносила в блек-лист URL-адреса из спамерских постов, не принимая впоследствии этот URL. После достижения некоторой массы, в сутки стало прорываться не более 5-10 спамерских попыток.
После внедрения данной простой функциональности я заскучал. На момент написания этих строк уже ни один не прорвался за двое суток. А максимально было 2 (две) записи, попавших в премодерацию. Теперь я, как в анекдоте, очень одинок, даже спамеры мне не пишут.
Имеется в виду результат работы совокупности двух подсистем: этой и проверки URL перед сохранением комментария.
В отшибании роботов запретом 403 тоже есть свой резон: имеется шанс, что даже самые тупые автопостинг-программы осознают бесперспективность попыток и и выкинут наш адрес из спамерских баз данных.
Теперь в логах сервера приход робота в 99% случаев выглядит так:
[01/Mar/2011:08:12:00 +0300] "GET /first-record/ HTTP/1.0" 200 12812 [01/Mar/2011:08:12:01 +0300] "POST /first-record/ HTTP/1.0" 403 31 [01/Mar/2011:08:12:31 +0300] "GET /first-record/ HTTP/1.0" 200 12812
Первая строчка — проверка на доступность страницы, которую собрались поспамить. Вторая — отправка POST-запроса со спамерским текстом и закономерный ответ сервера. Третья строка — бот проверяет результат своей деятельности, как видно — результата нет, так как размер страницы тот же самый (12812) и ничего нового на ней не появилось.
Примеры практического применения
Проверка только POST-запросов
require_once('functions/antibot.php'); session_start(); if (!antibot_check_post()) { // Если запрос "POST" и интервал меньше заданного, // то сохраним текущее время antibot_save_time(); // и пошлем робота куда подальше antibot_header(); exit(); } // Проверка пройдена, но все равно сохраним // текущее время - для следующей проверки. antibot_save_time(); /*... Остальной исполняемый код ...*/
Чтобы проверять по другому временному интервалу, используйте вызов функции antibot_check_post (или основную antibot_check_time) с параметром (типа float) в виде нужного вам интервала в секундах:
// Хотим вместо 5 секунд использовать 3 секунды if (!antibot_check_post(3)) { //... }
Миллисекунды в этом случае будут после десятичной точки: 3.25 соответствуют интервалу 3 секунды 250 миллисекунд, 0.3 — 300 миллисекунд.
Проверка всех запросов
Не рекомендуется к использованию, так как под раздачу могут попасть легальные роботы поисковых систем. Например, Aport.ru и Mail.ru ходят не периодически, а набегами, выкачивая за раз несколько адресов с интервалом запросов меньше секунды (2-3 запроса в секунду точно).
С другой стороны, так можно попытаться защититься от грабберов — автоматических сборщиков контента.
require_once('functions/antibot.php'); session_start(); if ((!antibot_check_post(3) || (!antibot_check_time(0.3))) { // Для "POST" и других запросов (GET, HEAD...) // временные интервалы разные antibot_save_time(); antibot_header(); exit(); } antibot_save_time(); /*... Остальной исполняемый код ...*/
Расширение функциональности
Так как все разбито на небольшие функции, то их легко совместить с дополнительными условиями и вставить дополнительный пистон негодяйскому спамеру. Представим, что у нас есть функция, определяющая, доступны-ли комментарии на данной странице (например, как-нибудь по URL, неважно):
function isComments() { // Доступны-ли комментарии на данной странице? //... } if ((isComments()) && (!antibot_check_post())) { antibot_save_time(); // Здесь можно записать IP негодяя в бан-лист antibot_header(); exit(); }
Кстати, если соберётесь кого банить по IP, лучше организовать хранение данных таким способом, чтобы у каждого подозрительного адреса был счётчик подозрительных попыток доступа. И только при достижении определённого числа (достаточно большого, например — 200) таких попыток, отшибать любые запросы с этого адреса. Ну и иметь список «белых» адресов, куда внести поисковики и себя. Это защитит вас от случайных срабатываний (ну мало-ли, быстрый пользователь попался) и от забанивания кучи IP-шников, если спамер работает с динамического IP (черт с ним тогда).
07.01.2011 | 22:55
