Ограничение на скорость выдачи страниц одному пользователю
Случилось мне однажды столкнуться с ситуацией, когда мой хостер предъявил мне претензию о том, что мой акаунт создаёт непомерно большую нагрузку на MySQL-сервер. Посмотрев логи, я заметил, что такую нагрузку создают программы-качалки, которые копируют сайт целиком на локальный компьютер. Во время обращения к странице происходит несколько sql-запросов к базе данных. А если учесть, что эти программы готовы скачивать сразу несколько страниц с сайта, то получается, что в секунду идёт от 3 до 10 запросов. При такой «атаке» серверу действительно приходится не сладко.
Решением я увидел ограничение доступа к сайту с одного ip-адреса чаще, чем один раз в 2 секунды. (Это значение можно регулировать). Проверка происходит без использования sql-сервера, поэтому идёт достаточно быстро.
Здесь я предлагаю php-скрипт, реализующий такую проверку.
Для подключение этого модуля необходимо сделать следующее:
- Сохранить модуль на сервер в любом доступном для скриптов месте.
- Создать на сервере директорию для временных файлов.
- Прописать в модуле путь к этой директории, заменив им строку tmp_path
- Вставить вызов модуля в начало тех скриптов, которые отвечают за отображение страницы. Нужно следить, чтобы модуль не вызывался из тех скриптов, которые обрабатывают действия пользователя и после этого делают автоматический редирект на себя же или на другие скрипты, в которых будет стоять вызов модуля. Это приведёт к срабатыванию защиты.
Вторую версию скрипта породила ошибка, появляющаяся при одновременном обращении к файлу, хранящему информацию о последних посетителях. Во второй версии посещения записываются в виде файлов нулевой длины в специальную временную директорию. Кроме того, изменён способ учёта интервала: если раньше интервал считался от первого обращения, то теперь от последнего. Таким образом, если раньше при «бомбардировке» сайта раз в несколько секунд страница всё-таки отдавалась, то теперь нормальный показ возобновится лишь после того, как будет полностью выдержан установленный интервал времени.
Третья версия скрипта включает в себя два коренных изменения. Во-первых, если посетитель продолжает атаковать ваш сайт, то через определённое количество запросов его ip-адрес будет внесёт в «чёрный список» файла .htaccess, и ему будет полностью закрыт доступ к сайту. Запрет через .htaccess практически не отнимает у процессора время. Во-вторых, проверка захода поисковых роботов теперь ведётся не по полю HTTP_USER_AGENT, а по ip-адресу. Для этого есть две причины. Первая из них — это то, что, например, поисковая система Aport не всегда подписывается. Наверное, это из-за того, что они боятся, что для их робота будут выдаваться другие страницы, нежели для посетителя-человека. Интересно, что более популярные поисковики не опускаются до такой паранойи. Вторая причина в том, что некоторые программы-качалки ухитряются выдавать себя за поисковых роботов. А вот это уже серьёзно. Поэтому было принято решение пропускать мимо этого скрипта все запросы с ip-адресов, принадлежащим компаниям-поисковикам, т.к. нет уверенности в точных адресах роботов и в том, что эти адреса не будут меняться.
Четвёртая версия включает в себя запрос капчи. Дело в том, что настроив программу скачивания таким образом, чтобы она делала запросы не чаще, чем минимальный возможный интервал, можно получить сайт, скажем, в 1000 страниц всего за 33 минуты. Теперь проверка усложняется. Задаётся время длительности нахождения на сайте (5 минут). Если пользователь делает новый запрос меньше, чем через 5 минут, значит считается, что это длится одна сессия. Если больше — новый заход. Если пользователь за одну сессию сделал определённое количество запросов (50, например), ему предлагается ввести содержимое капчи. И эта капча предлагается ему в течение заданного времени (1 час). Если он так и не ввёл её, то информация об этом пользователе сбрасывается. Таким образом, у робота есть возможность либо запрашивать страницу 1 раз в 5,5 минут, тогда 1000 страниц он скачает за 91 час, т.е. 3 суток и 19 часов, либо пережидать запрос капчи: 33 минуты + (1000/50) = 20 ч. 33 мин. Варьируя этими числами можно добиться нужного результата.
Эта версия включает в себя 2 файла: сам скрипт защиты и скприпт показа капчи. Кроме того, в комплект входит реализация капчи — Kcaptcha.
- Код: Выделить всё
<?php
/*
*--------------------------------------------------------
* Модуль antiddos.php V4.2.8 Ср 09 Июль 2014
* Copyright (C) Андрей Якушев, 2006. http://avy.ru
*--------------------------------------------------------
* Модуль предназначен для ограничения доступа к сайту или
* к страницам, где он включён.
* Принцип работы в том, что запоминается ip-адрес и время
* обращения с этого адреса. И если в течение заданного
* времени происходит обращение с того же адреса, то ему
* выдаётся ошибка 503.
* Если количество недопустимых обращений подряд превышает
* определённое число, ip-адрес закрывается через файл
* .htaccess
* Если пользователь длительное время находится на сайте,
* требуется ввод капчи.
* Модуль необходимо подключать к скрипту самым первым.
* Этим обеспечивается быстрота его работы.
*--------------------------------------------------------
*/
/* Директория для временных файлов. Необходимо указать
отдельную директорию, т.к. большое количество файлов
в одной папке замедляет скорость обращения к ней. */
define ('AD_DIRNAME', $_SERVER['DOCUMENT_ROOT'] . '/tmp_path');
/* Время задержки в секундах, в течение которого нельзя
обращаться к сайту. */
define ('AD_DELAY', 2);
/* Количество запрещённых повторений, после которых ip-адрес
будет забанен. Нужно обратить внимание на то, что некоторые
программы чтения RSS-каналов считывают все ссылки, помещённые
в канале сразу. Поэтому, если на сайте есть такие каналы,
это число необходимо поставить больше, чем максимальное
количество элементов в канале. */
define ('AD_TRYING', 35);
/* Ip-адрес пользователя. В настройках разных серверов он может
содержаться в разных переменных. Здесь нужно вставить именно
ту, которая будет содержать реальный адрес пользователя. */
define ('USER_IP', $_SERVER['REMOTE_ADDR']);
/* Имя файла, в котором храниться информация о длительном нахождении
на сайте. ВНИМАНИЕ! Перед использованием скрипта, убедитесь, что
файл существует. Иначе выскочит ошибка. */
define ('AD_FILE_NAME', 'access.txt');
/* Интервал времени, когда считается, что человек всё ещё находится
на сайте в минутах (5 мин). */
define ('AD_LIVE_TIME', 300);
/* Количество обращений, через которое нужно спрашивать капчу */
define ('AD_COUNT_REQUEST', 50);
/* Количество времени, через которое нужно спрашивать капчу в
минутах (60 мин.). Это же время будет ожидаться ввод капчи. */
define ('AD_TIME_DISPLAY_CAPTHCA', 3600);
/* Выбранный режим:
0 - не использовать капчу;
1 - считать количество обращений;
2 - замечать временой интервал. */
define ('AD_CHECK_MODE', 1);
/* Пропустить количество кликов,
прежде чем вывести ошибку 503 */
define ('AD_TRYING_CLICK', 0);
/*
*---------------------------------------------------------------
* Список поисковых роботов.
* Очень не хорошо, если поисковый робот будет натыкаться
* на ошибки на сайте. Ему это может сильно не понравиться.
* Поэтому пишем список ip-адресов роботов; добавляем или
* удаляем, что нужно.
* IP-адреса:
* Alta Vista - см. Yahoo
* Aport - 194.67.18.
* Gigabot - 66.231.188. (66.231.188.0/24)
* Google - 209.85.128.0 - 209.85.255.255 (209.85.128.0/17),
* 72.14.192.0 - 72.14.255.255 (72.14.192.0/18),
* 66.249.64.0 - 66.249.95.255 (66.249.64.0/19),
* 64.68.80.0 - 64.68.87.255 (64.68.80.0/21),
* 66.102.0.0 - 66.102.15.255 (66.102.0.0/20),
* 64.233.160.0 - 64.233.175.255 (64.233.160.0/19),
* 216.239.32.0 - 216.239.63.255 (216.239.32.0/19)
* Mail.Ru - 195.239.211.0 (195.239.211.0/24),
* 94.100.181.128 - 94.100.181.255 (94.100.181.128/25),
* 217.69.128.0 - 217.69.143.255 (217.69.128.0/20)
* msnbot - 65.52.0.0 - 65.55.255.255 (65.52.0.0/14),
* 207.46.0.0 - 207.46.255.255 (207.46.0.0/16)
* 157.54.0.0 - 157.60.255.255 (157.54.0.0/15, 157.56.0.0/14, 157.60.0.0/16)
* Rambler - 81.19.64.0 - 81.19.66.255 (81.19.64.0/19)
* Yahoo - 74.6.0.0 - 74.6.255.255 (74.6.0.0/16),
* 69.147.64.0 - 69.147.127.255 (69.147.64.0/18)
* 72.30.64.0 - 72.30.255.255 (72.30.0.0/16)
* 67.195.0.0 - 67.195.255.255 (67.195.0.0/16)
* 68.180.128.0 - 68.180.255.255 (68.180.128.0/17)
* Yandex - 213.180.214.128 - 213.180.214.255 (213.180.192.0/19),
* 77.88.22.0 - 77.88.23.255 (77.88.0.0/18)
* 93.158.128.0 - 93.158.191.255 (93.158.128.0/18)
* 95.108.128.0 - 95.108.255.255 (95.108.128.0/17)
* 87.250.224.0 - 87.250.255.255 (87.250.224.0/19)
* 178.154.128.0 - 178.154.255.255 (178.154.128.0/17)
* 84.201.128.0 - 84.201.191.255 (84.201.128.0/18)
* 5.255.192.0 - 5.255.255.255 (5.255.192.0/18)
* 141.8.128.0 - 141.8.191.255 (141.8.128.0/18)
* 37.140.128.0 - 37.140.191.255 (37.140.128.0/18)
* LiveInternet - 88.212.202.0 - 88.212.202.63 (88.212.202.0/26)
* OpenStat - 193.169.234.0 - 193.169.234.255 (193.169.234.0/24)
* Ip-адреса и маски к ним кодируются в виде строки из 4 символов
* Это сделано из-за того, что невозможно в PHP использовать
* стандартными (простыми) процедурами 32-битное целое число
* без знака. А использование специальных библиотек усложнит
* работу и сделает скрипт зависимым от этих библиотек.
*---------------------------------------------------------------
*/
$ad_Robots_IP = array(
'Localhost' => array(
sprintf('%c%c%c%c', 127, 0, 0, 1),
sprintf('%c%c%c%c', 255, 255, 255, 255)
),
'Aport' => array(
sprintf('%c%c%c%c', 194, 67, 18, 0),
sprintf('%c%c%c%c', 255, 255, 255, 0)
),
'Gigabot' => array(
sprintf('%c%c%c%c', 66, 231, 188, 0),
sprintf('%c%c%c%c', 255, 255, 255, 0)
),
'Google1' => array(
sprintf('%c%c%c%c', 209, 85, 128, 0),
sprintf('%c%c%c%c', 255, 255, 128, 0)
),
'Google2' => array(
sprintf('%c%c%c%c', 72, 14, 192, 0),
sprintf('%c%c%c%c', 255, 255, 192, 0)
),
'Google3' => array(
sprintf('%c%c%c%c', 66, 249, 64, 0),
sprintf('%c%c%c%c', 255, 255, 224, 0)
),
'Google4' => array(
sprintf('%c%c%c%c', 64, 68, 80, 0),
sprintf('%c%c%c%c', 255, 255, 248, 0)
),
'Google5' => array(
sprintf('%c%c%c%c', 66, 102, 0, 0),
sprintf('%c%c%c%c', 255, 255, 240, 0)
),
'Google6' => array(
sprintf('%c%c%c%c', 64, 233, 160, 0),
sprintf('%c%c%c%c', 255, 255, 224, 0)
),
'Google7' => array(
sprintf('%c%c%c%c', 216, 239, 32, 0),
sprintf('%c%c%c%c', 255, 255, 224, 0)
),
'Mail.Ru1' => array(
sprintf('%c%c%c%c', 195, 239, 211, 0),
sprintf('%c%c%c%c', 255, 255, 255, 0)
),
'Mail.Ru2' => array(
sprintf('%c%c%c%c', 94, 100, 181, 128),
sprintf('%c%c%c%c', 255, 255, 255, 128)
),
'Mail.Ru3' => array(
sprintf('%c%c%c%c', 217, 69, 128, 0),
sprintf('%c%c%c%c', 255, 255, 240, 0)
),
'msnbot1' => array(
sprintf('%c%c%c%c', 65, 52, 0, 0),
sprintf('%c%c%c%c', 255, 252, 0, 0)
),
'msnbot2' => array(
sprintf('%c%c%c%c', 207, 46, 0, 0),
sprintf('%c%c%c%c', 255, 255, 0, 0)
),
'msnbot3' => array(
sprintf('%c%c%c%c', 157, 54, 0, 0),
sprintf('%c%c%c%c', 255, 254, 0, 0)
),
'msnbot4' => array(
sprintf('%c%c%c%c', 157, 56, 0, 0),
sprintf('%c%c%c%c', 255, 252, 0, 0)
),
'msnbot5' => array(
sprintf('%c%c%c%c', 157, 60, 0, 0),
sprintf('%c%c%c%c', 255, 255, 0, 0)
),
'Rambler' => array(
sprintf('%c%c%c%c', 81, 19, 64, 0),
sprintf('%c%c%c%c', 255, 255, 224, 0)
),
'Yahoo1' => array(
sprintf('%c%c%c%c', 74, 6, 0, 0),
sprintf('%c%c%c%c', 255, 255, 0, 0)
),
'Yahoo2' => array(
sprintf('%c%c%c%c', 69, 147, 64, 0),
sprintf('%c%c%c%c', 255, 255, 192, 0)
),
'Yahoo3' => array(
sprintf('%c%c%c%c', 72, 30, 0, 0),
sprintf('%c%c%c%c', 255, 255, 0, 0)
),
'Yahoo4' => array(
sprintf('%c%c%c%c', 67, 195, 0, 0),
sprintf('%c%c%c%c', 255, 255, 0, 0)
),
'Yahoo5' => array(
sprintf('%c%c%c%c', 68, 180, 128, 0),
sprintf('%c%c%c%c', 255, 255, 128, 0)
),
'Yandex1' => array(
sprintf('%c%c%c%c', 213, 180, 192, 0),
sprintf('%c%c%c%c', 255, 255, 224, 0)
),
'Yandex2' => array(
sprintf('%c%c%c%c', 77, 88, 0, 0),
sprintf('%c%c%c%c', 255, 255, 192, 0)
),
'Yandex3' => array(
sprintf('%c%c%c%c', 93, 158, 128, 0),
sprintf('%c%c%c%c', 255, 255, 192, 0)
),
'Yandex4' => array(
sprintf('%c%c%c%c', 95, 108, 128, 0),
sprintf('%c%c%c%c', 255, 255, 128, 0)
),
'Yandex5' => array(
sprintf('%c%c%c%c', 87, 250, 224, 0),
sprintf('%c%c%c%c', 255, 255, 224, 0)
),
'Yandex6' => array(
sprintf('%c%c%c%c', 178, 154, 128, 0),
sprintf('%c%c%c%c', 255, 255, 128, 0)
),
'Yandex7' => array(
sprintf('%c%c%c%c', 84, 201, 128, 0),
sprintf('%c%c%c%c', 255, 255, 192, 0)
),
'Yandex8' => array(
sprintf('%c%c%c%c', 5, 255, 192, 0),
sprintf('%c%c%c%c', 255, 255, 192, 0)
),
'Yandex9' => array(
sprintf('%c%c%c%c', 141, 8, 128, 0),
sprintf('%c%c%c%c', 255, 255, 192, 0)
),
'Yandex10' => array(
sprintf('%c%c%c%c', 37, 140, 128, 0),
sprintf('%c%c%c%c', 255, 255, 192, 0)
),
'LiveInternet' => array(
sprintf('%c%c%c%c', 88, 212, 202, 0),
sprintf('%c%c%c%c', 255, 255, 255, 192)
),
'OpenStat' => array(
sprintf('%c%c%c%c', 193, 169, 234, 0),
sprintf('%c%c%c%c', 255, 255, 255, 0)
),
);
/*
*----------------------------------------------------------
* Функция создаёт в указанной директории файл, начинающийся
* с буквы a (для отличия от других возможных файлов) и
* содержащий в имени ip-адрес клиента.
* Если количество обращений подряд с данного адреса
* превысило допустимый предел, адрес закрывается через файл
* .htaccess
* При желании может быть отослано письмо на специальный
* адрес, в котором будет передана информация о действиях
* с заблокированного адреса.
*----------------------------------------------------------
*/
function ad_WriteIP($counter)
{
$counter++;
if ($counter > AD_TRYING)
{
//Баним ip-адрес
$f = fopen($_SERVER['DOCUMENT_ROOT'] . '/.htaccess', 'a');
fwrite($f, "\n# " . date('Y-m-d H:i:s') . "\ndeny from " . USER_IP);
fclose($f);
// Открыть комментарии, если нужно уведомлять по почте
/*
//Получаем хост (в некоторых логах он может быть вместо ip
$host = gethostbyaddr(USER_IP);
$mess = 'Заблокирован адрес ' . USER_IP .
' (' . $host . ")\n\n";
//Выполняем запрос к логам. Нужно указать путь и имя лог-файла
exec('cat /home/your_dir/logs/access_log | egrep \'(' .
str_replace('.', '\\.', USER_IP) . ')|(' .
str_replace('.', '\\.', $host) . ')\' | sort -k 4 >' .
AD_DIRNAME . '/dump.txt');
$mess .= file_get_contents(AD_DIRNAME . '/dump.txt');
@ unlink(AD_DIRNAME . '/dump.txt');
$email = 'my_box@myhost.ru'; //Укажите свой e-mail
mail($email, 'IP-address was banned', $mess, "From: " . $email .
"\nReply-To: " . $email .
"\nContent-Type: text/plain; charset=Windows-1251" .
"\nContent-Transfer-Encoding: 8bit");
*/
}
else
{
//Записываем файл с ip-адресом и количеством обращений
$f = fopen(AD_DIRNAME . '/a' . USER_IP . '_' .
$counter, 'w');
fclose($f);
}
}
/*
*----------------------------------------------------
* Проверка на отношение ip-адреса к сетям поисковиков
*----------------------------------------------------
*/
$ad_IsRobot = false;
$ad_IP = explode('.', USER_IP);
$ad_IPMatch = sprintf('%c%c%c%c', $ad_IP[0], $ad_IP[1], $ad_IP[2], $ad_IP[3]);
foreach ($ad_Robots_IP as $ad_botname => $ad_match)
{
//Если на входящий адрес наложить маску операцией "и",
//то он должен будет совпасть с начальным адресом сети.
if (($ad_IPMatch & $ad_match[1]) == $ad_match[0])
{
$ad_IsRobot = $ad_botname;
break;
}
}
/*
*---------------------------------------------------------
* Поисковые роботы не любят, когда к адресу страницы
* добавляется переменная сессии. Поэтому, если на сайте
* используются сессии, то их лучше включать, если агент -
* не робот.
* Если сессии не используются, то этот кусок можно убрать.
*---------------------------------------------------------
*/
/*
if (!$ad_IsRobot || ($ad_IsRobot == 'Localhost'))
{
session_start();
}
else
{
//Чтобы поисковый робот не сильно загружал сайт,
//делаем ему задержку. Но лучше всего вместо этого
//использовать команду Crawl-delay: в файле robots.txt
sleep(AD_DELAY);
}
*/
/*
*-----------------------------------------------
* Функция проверяет необходимость вывода капчи.
* Если капча необходима, делает редирект на неё.
*-----------------------------------------------
*/
function ad_Check_captcha()
{
$filename = AD_DIRNAME . '/' . AD_FILE_NAME;
//ожидаем пока файл станет доступен для записи
while (!($fh = fopen($filename, 'r+')))
{
}
//Закроем файл для доступа другим пользователям
@ flock($fh, LOCK_EX);
$contents = @ fgets($fh);
if ($contents)
{
$list = unserialize($contents);
}
else
{
$list = array();
}
$need_kaptcha = false;
if(is_array($list))
{
//Если содержимое получено и оно нужного вида
//Получим метку времени, до которой надо удалить
//элементы массива
$dedline = time() - AD_LIVE_TIME;
$waiting = time() - AD_TIME_DISPLAY_CAPTHCA;
foreach ($list as $ip => $data)
{
//Удаляем все просроченные элементы кроме тех, что ожидают
//капчу; признак вызова капчи - отрицательное число в
//количестве показов.
if (($data['t'] <= $dedline) && ($data['c'] > 0)
|| (($data['c'] <= 0) && (($data['t'] <= $waiting))))
{
unset($list[$ip]);
}
}
if (isset($list[USER_IP]))
{
//Определяем, нужно ли запрашивать капчу
$need_kaptcha = ($list[USER_IP]['c'] <= 0)
|| ((AD_CHECK_MODE == 1) && ($list[USER_IP]['c'] >= AD_COUNT_REQUEST))
|| ((AD_CHECK_MODE == 2) && ($list[USER_IP]['s'] <= $waiting));
//Увеличиваем счётчик, если он не сброшен для показа капчи
if ($need_kaptcha)
{
$list[USER_IP]['c'] = -1;
}
else
{
$list[USER_IP]['c']++;
}
//Устанавливаем время обращения
$list[USER_IP]['t'] = time();
}
else
{
//Если записи ip нет, создаём новую
$list[USER_IP] = array(
's' => time(),
't' => time(),
'c' => 1
);
}
}
rewind($fh);
// записываем в файл серилизованый массив
fwrite($fh, serialize($list));
fclose($fh);
if ($need_kaptcha)
{
header('Location: /captcha.php?back_url=' . $_SERVER['REQUEST_URI']);
}
}
if (!$ad_IsRobot)
{
// Чтение каталога и удаление старых файлов
$ad_dir = opendir(AD_DIRNAME) or
die('Отсутствует директория для временных файлов');
$ad_forbid = time() - AD_DELAY;
//IP-адрес в имени файла, начинающегося на букву a,
//а время обращения - время изменения файла
$ad_before_trying = 0;
while (false !== ($ad_FName = readdir($ad_dir)))
{
if (preg_match('|^a[1-9]|', $ad_FName))
{
if (@ filemtime(AD_DIRNAME . '/' . $ad_FName) < $ad_forbid){
@ unlink(AD_DIRNAME . '/' . $ad_FName);
}
elseif (preg_match('|^a' . str_replace('.', '\\.',
USER_IP) . '_([0-9]+)$|', $ad_FName, $ad_match))
{
//Если файл есть, то читаем, сколько раз к нему обращались
$ad_before_trying = intval($ad_match[1]);
@ unlink(AD_DIRNAME . '/' . $ad_FName);
}
}
}
closedir($ad_dir);
// Выводить или не выводить сообщение об ошибке
if ($ad_before_trying > AD_TRYING_CLICK)
{
$ad_sleep = AD_DELAY * 2;
// Если обращение было недавно, то выводим сообщение об ошибке
header ('HTTP/1.0 503 Service Unavailable');
header ('Status: 503 Service Unavailable');
header ('Retry-After: ' . $ad_sleep);
?>
<!doctype html public "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>Ошибка 503</title>
<meta http-equiv="Content-Type" content="text/html; charset=Windows-1251" />
<script type="text/javascript"> <!--
function Delay()
{
setTimeout("document.getElementById('go').style.display = 'block'",
(<?=$ad_sleep?> * 1000));
}
//-->
</script>
</head>
<body onLoad="Delay();">
<h1>Сервер временно недоступен</h1>
<h2>Ошибка 503 (Service Unavailable)</h2>
<p>Сервер временно не может в данный момент выдать запрашиваемую Вами страницу.
Через <?=$ad_sleep?> сек. запрашиваемая Вами страница будет показана.
Если это не произошло, нажмите клавишу F5.</p>
<div id="go" style="display: none;"><br />
<a href="<?=$_SERVER['REQUEST_URI']?>">Перейти на запрашиваемую страницу</a>
</div>
</body>
</html>
<?php
ad_WriteIP($ad_before_trying); // Перед выходом записываем ip
exit;
}else{
ad_WriteIP($ad_before_trying);
}
if (AD_CHECK_MODE)
{
ad_Check_captcha();
}
}
?>
Второй файл - captcha.php лучше разместить в корне сайта, чтобы он был доступен посетителям.
- Код: Выделить всё
<?php
/*
*--------------------------------------------------------
* Модуль captcha.php V1.0.0 Чт 31 Март 2011
* Copyright (C) Андрей Якушев, 2011. http://avy.ru
* Входит в состав antiddos.php V4.0.0
*--------------------------------------------------------
* Модуль предназначен для запроса капчи, если пользователь
* превысил допустимые условия нахождения на сайте.
* Модуль сбрасывает условия, если пользователь правильно
* ввёл капчу.
*--------------------------------------------------------
*/
/* Директория для временных файлов. Необходимо указать
отдельную директорию, т.к. большое количество файлов
в одной папке змедляет скорость обращения к ней. */
define ('AD_DIRNAME', $_SERVER['DOCUMENT_ROOT'] . '/tmp_path');
/* Ip-адрес пользователя. В настройках разных серверов он может
содержаться в разных переменных. Здесь нужно вставить именно
ту, которая будет содержать реальный адрес пользователя. */
define ('USER_IP', $_SERVER['REMOTE_ADDR']);
/* Имя файла, в котором храниться информация о длительном нахождении
на сайте. ВНИМАНИЕ! Перед использованием скрипта, убедитесь, что
файл существует. Иначе выскочит ошибка. */
define ('AD_FILE_NAME', 'access.txt');
session_start();
if ((isset($_SESSION['captcha_keystring']) && $_POST['key'])
&& ($_SESSION['captcha_keystring'] == $_POST['key']))
{
//проверка пройдена успешно. Удаляем запись из файла
$filename = AD_DIRNAME . '/' . AD_FILE_NAME;
while (!($fh = fopen($filename, "r+")))
{
}
//Закроем файл для доступа другим пользователям
@ flock($fh, LOCK_EX);
$contents = @ fgets($fh);
if ($contents)
{
$list = unserialize($contents);
}
else
{
$list = array();
}
if(is_array($list))
{
unset($list[USER_IP]);
}
rewind($fh);
// записываем в файл серилизованый массив
fwrite($fh, serialize($list));
fclose($fh);
header('Location: ' . $_GET['back_url']);
}
else
{
?>
<html>
<head>
<META NAME="GOOGLEBOT" CONTENT="NOARCHIVE">
<META HTTP-EQUIV="CACHE-CONTROL" CONTENT="NO-CACHE">
<META HTTP-EQUIV="PRAGMA" CONTENT="NO-CACHE">
<meta http-equiv="Content-Type" content="text/html; charset=Windows-1251" />
<meta name="robots" content="noindex,nofollow" />
<title>Проверка пользователя</title>
<body>
Уважаемый посетитель!<br />
Приносим свои извинения. В целях проверки того, что к сайту обращается человек,
а не программа, введите пожалуйста текст, изображённый на картинке.<br />
<img src="/kcaptcha/" ><br />
<form action="" method="post">
<input type="text" name="key" ><br />
<input type="submit" value="Отправить" >
</form>
</body></html>
<?
}
?>
Также в корень сайта нужно выложить папку, находящуюся в архиве: