Цитата мудреца

Голосование

Система Orphus. Если вы заметили ошибку на сайте, нажмите сюда.
Загружается, подождите...
Начало сайта Материалы сайта Программы PHP-скрипты
Версия для слабовидящих
Версия для печати

Ограничение на скорость выдачи страниц одному пользователю

Здесь представлены некоторые мои разработки на PHP. Это в, основном, служебные скрипты, которые работают в составе других скриптов и не могут быть протестированы здесь непосредственно.

Прописывание новых диапазонов поисковиков

Сегодня получил такое письмо:
скрипт начал блокировать Яндекс по ip

Служба поддержки Яндекса написала мне :
Нашему роботу отдается код ошибки 403, возможно, это работа системы защиты. Возможно Вы можете поместить IP нашего робота в белый список. Наш робот ходит к Вам с IP 93.158.149.31

Подскажите пожалуйста, как прописать ПРАВИЛЬНО этот ip чтоб скрипт не блокировал Яндекс никогда.

Прежде всего хочу поблагодарить всех, кто шлёт мне такие письма. Благодаря им я дополняю скрипт новыми диапазонами адресов. Дело в том, что мало того, что адреса время от времени меняются, ещё и товарищи поисковики не афишируют свои диапазоны. Поэтому у меня стоит контроль на блокировку - присылается письмо с информацией о заблокированном адресе. А я уже смотрю по Whois, кто это такой, чтобы, если что, добавить поисковика в список исключений.

Скрипт изменил. Смотрите на сайте.

Добавил следующую строчку:

Код: Выделить всё
  'Yandex3'   => array(
      sprintf('%c%c%c%c', 93, 158, 128, 0),
      sprintf('%c%c%c%c', 255, 255, 192, 0)
   ),

Как это делается? Захожу на страницу сервиса Whois и читаю, внизу:
route: 93.158.128.0/18
descr: Yandex enterprise network
origin: AS13238
mnt-by: YANDEX-MNT
changed: gbragin@yandex-team.ru 20080602
source: RIPE

Там есть и другие диапазоны, но они более узкие, включающие в себя и этот. Этот самый широкий.
Теперь, как из этой записи получить то, что я вставил?
Берём первый адрес и раскладываем его в двоичный вид:
Код: Выделить всё
      93      158      128        0
01011101 10011110 10000000 00000000

Теперь рисуем 18 единиц слева
Код: Выделить всё
11111111 11111111 11000000 00000000

Теперь переводим это в десятичный вид:
Код: Выделить всё
     255      255      192        0

Получается, что всё, что в маске единицы - изменяться не может. А всё, что нули - может. Соответственно, максимальный адрес вхождения в эту маску:
Код: Выделить всё
01011101 10011110 10111111 11111111
      93      158      191      255

Получается, что адрес 93.158.149.31 входит в этот диапазон.
Ответить


На днях случилась такая переписка:
У меня вопрос по скрипту выложенному у Вас на сайте "Ограничение на скорость выдачи страниц одному пользователю"
- Можно ли данный скрипт дополнить функцией ограничение одновременного доступа на сайт (определённый домен) более N-го количества пользователей с разными IP, т.е. при N = 18 на сайт www.avy.ru девятнадцатый (19) IP попасть не может пока сайт не покинет кто то из первых 18 IP.

Если это можно реализовать, то я готов приобрести у Вас подобный скрипт. Думаю что многим бы он был интересен.

Скажите, что Вы называете "не покинет сайт"? Скрипт формирует страницу за 0,2 сек. как только страница сформирована, и отдана пользователю, все соединения его с сайтом закрыты. Уже считается, что пользователь покинул сайт.

У Вас в описании сказано - когда мой хостер предъявил мне претензию о том, что мой акаунт создаёт непомерно большую нагрузку на MySQL-сервер. Посмотрев логи, я заметил, что такую нагрузку создают программы-качалки, которые копируют сайт целиком на локальный компьютер.

И соответственно скрипт делает ограничение по обращению к MySQL-серверу с одного IP, т.е. я не могу открыть более одной страницы за две секунды (если 2 сек - Время задержки в секуднах, в течение которого нельзя обращаться к сайту.). Я говорю о варианте, когда я со своего IP и в этот же момент ещё допустим 100 человек со своих 100 IP производят обращение допустим на главную страницу сайта и в этом случае Ваш скрипт насколько я понимаю не работает, т.к. все IP обратились один раз и все IP разные и соответственно ограничивать нечего, хотя на MySQL-сервер идёт также большая нагрузка о которой Вами говорилось в первом абзаце.
Если в двух словах то Ваш вариант - ограничение большого количества запросов на MySQL-сервер с одного IP (вариант программы-качалки) одним обращением в 2 секунды. То о чём говорю я - ограничение большого количества запросов на MySQL-сервер с большого количества IP адресов одновременно. Если скрипт формирует страницу за 0.2 сек значит ограничить запросы чтобы их было не более 6 допустим за каждые 0.2 сек. Т.е. 100 чел с разных IP одновременно послали запрос и соответственно только 6 из них получили ответ, а остальные сообщение о перегрузке сервера с просьбой повторить попытку через 5 сек.

Я просто в этом не специалист и не знаю сколько времени уходит на формирование страницы скриптом, наверное разные скрипты за разное время формируют страницу, но суть в этом - ограничении одновременного обращения на MySQL-сервер в пределах 6 с разных IP.

У Вас реально стоит эта проблема, или Вы чисто гипотетически её предвидите?
Дело в том, что хостеры (конечно, все по-разному) считают усреднённую нагрузку на сервер за какой-то период времени, например, за 15 минут. И если эта нагрузка превышает, скажем, 5% с Вашего акаунта, то они применяют к Вам меры. Но это не значит, что в какие-то пиковые периоды она не может выскакивать выше. 5% подразумевает и то, что 2 минуты (!) ваш акаунт будет забирать 100% процессорного времени, а остальные 13 минут бездействовать. Что происходит, когда идёт большой наплыв посетителей, и сервер не успевает им отдавать страницы? Посетители становятся в очередь. Как только сервер освобождается, он начинает их обрабатывать. А посетитель видит лишь то, что страница отдаётся медленно. Ведь для того, чтобы показать пользователю страницу, сервер обрабатывает не один запрос, а несколько (каждая картинка - это запрос).

Вообще, 6 уникальных посетителей за 0,2 сек. - это очень круто! Если это просто пик, то сервер сам справится с этим пиком. Если же это постоянный наплыв, то это получается около 108000 в час. Лента.ру лишь за 8 часов такое количество посетителей приняла.

Кроме того, надо помнить, что любой скрипт, в т.ч. и защищающий сервер от лишней нагрузки, тоже создаёт нагрузку.

Если у Вас действительно большой наплыв посетителей, то лучше перейти на выделенный сервер.
Ответить


Проблема стоит реально, т.к. месяц назад мой сайт подвергся атаке и был отключен хостером, но удалось быстро (через 5 часов включили) с ними договорится о его включении. После чего стал искать скрипт ограничивающий многочисленный доступ к сайту пользователей для того чтобы снизить нагрузку и наткнулся на Ваш скрипт, установил его проверил все функционировало.
Вчера подвергся повторной атаке по заявлению хостера - 09.06.2009 в 19:37 заблокирован Нарушение правил хостинга, превышение кол-ва подключений (433) хотя по статистике liveinternet у меня в среднем 200 уникальных IP и 400 просмотров в сутки, т.е. на лицо явная скорее всего ddos атака и сдесь мне не помог Ваш скрипт в том исполнений в котором он сейчас, поэтому я и обратился к Вам с просьбой доработки.

Ну прежде всего, нужно разбираться.
Нужно тщательно проанализировать логи апача. Доверять liveinternet нельзя, потому что он анализирует лишь загруженные страницы.
А по логам апача можно многое узнать. В частности, кто и как вёл себя на сайте. Только после этого можно выработать алгоритм отлова этих мерзавцев.
Прежде всего хочу сказать, что мой скрипт не поможет защитить ЦП от нагрузки. Скорее сам добавит нагрузку. А вот SQL-сервер защитит. Если речь идёт о количестве подключений к серверу БД, то можно переработать скрипт. Но я думаю, что если действительно речь идёт о ДДОС-атаке, то до БД дело не доходило вообще.
Начните с логов. Посмотрите их, найдите проблемный кусок и пришлите его мне. Подумаем вместе, что с этим можно сделать.
Ответить


Re: Ограничение на скорость выдачи страниц одному пользователю

Спасибо за скрипт, при установке его пришлось исправить спец символы на скобки в явном виде, почему то апач ругался на них.

Такая проблема: есть сайт в википедией, там скрипт работает как и должен, а вот при попытке установки на форуме пришлось делать такие ухищрения:
Код: Выделить всё
$_SERVER['DOCUMENT_ROOT'] = '/home/dia-clubru/www/';
т.е. почему то эта переменная вела
Код: Выделить всё
/usr/local/apache/htdocsincludes/

ну с этим справился, но некоторые пользователи упрямо получали вывод 503-й ошибки, в бан лист они не попадали, но и прочитать информацию не могли, прочитал пост в данной ветке о подобном поведении, но, как я понял, там это касалось всех, в моем же случае у одних форум работал как и должен, у других 503-я выскакивала через раз, вне завивисимости от их активности, третьи же видели ее постоянно.

Может чего подскажете?

P.S. Сессии в Вашем файле не включал (не разкоментировал), хотя на форуме они используются.
Ответить


Connie писал(а):при установке его пришлось исправить спец символы на скобки в явном виде, почему то апач ругался на них

Можете код привести Ваш, что именно исправили? Интересно...

Connie писал(а):некоторые пользователи упрямо получали вывод 503-й ошибки

Сложно сказать причину. Нужно ковыряться, пробовать...
Есть одна мысль: такое может появляться, когда идёт автоматический редирект, т.е. скрипт вызывается (модуль отрабатывается), потом скрипт делает редирект на другую страницу, а там, в свою очередь опять запускается модуль, который понимает это, как повторный вызов через недопустимо малый промежуток времени. Поэтому может и появляться ошибка.
А не банит, потому что бан происходит после определённого количества 503-х ошибок подряд..

Вот как я решил проблему с редиректом. У меня редирект происходит через специальную функцию:
Код: Выделить всё
function Redirect($target){
   $f_name   = $_SERVER['DOCUMENT_ROOT'] . $TmpPath . '/r' . $_SERVER['REMOTE_ADDR'];
   $f      = fopen($f_name, 'w');
   fclose($f);
   header('Location: ' . $target);
   exit;
}

А в самом модуле стоит такая проверка:
Код: Выделить всё
$ad_IsRedir      = false;
$ad_TicketFname = AD_DIRNAME . '/r' . $_SERVER['REMOTE_ADDR'];
if (file_exists($ad_TicketFname))
{
   $ad_IsRedir = true;
   unlink($ad_TicketFname);
}

if (!$ad_IsRobot && !$ad_IsRedir)
{
   /*** Чтение каталога и удаление старых файлов ***/
.........
Ответить


Re: Ограничение на скорость выдачи страниц одному пользователю

Можете код привести Ваш, что именно исправили? Интересно...

У Вас на первой станице:
Код: Выделить всё
$ad_Robots_IP = array(
   'Aport'      => array(

Я сделал так:
Код: Выделить всё
$ad_Robots_IP = array(
   'Aport'      => array(
ну и там где идет сравнение так же заменил, разумеется нужные скобки ставил :) Т.е. тут
Код: Выделить всё
if ($counter > AD_TRYING)
   {

А тут
Код: Выделить всё
if (@ filemtime(AD_DIRNAME . '/' . $ad_FName) < $ad_forbid){
            @ unlink(AD_DIRNAME . '/' . $ad_FName);
         }

по другому не шло, но я в php не особо, точнее никак :) точнее этот язык не знаю, а программировать могу.

Вот тут
Код: Выделить всё
while (false !== ($ad_FName = readdir($ad_dir)))
   {
верно сравнение записно? Не следует писать != :roll:

Код: Выделить всё
Поэтому может и появляться ошибка.

То есть мне нужно в коде скрипта добавлять

Код: Выделить всё
function Redirect($target){
   $f_name   = $_SERVER['DOCUMENT_ROOT'] . $TmpPath . '/r' . $_SERVER['REMOTE_ADDR'];
   $f      = fopen($f_name, 'w');
   fclose($f);
   header('Location: ' . $target);
   exit;
}
....
//и изменить существующий код так:
$ad_IsRedir      = false;
$ad_TicketFname = AD_DIRNAME . '/r' . $_SERVER['REMOTE_ADDR'];
if (file_exists($ad_TicketFname))
{
   $ad_IsRedir = true;
   unlink($ad_TicketFname);
}

if (!$ad_IsRobot && !$ad_IsRedir)
{
   /*** Чтение каталога и удаление старых файлов ***/
.........

Или же функцию редиректа искать в коде скрипта, выдающего страницу?

А может ли быть такое, что нескольких пользователей принимаю за одного? Вряд ли конечно :|

И вот еще нюанс, у нас два сайта wiki.имя домена.ru и www.имя домена.ru/forum_ru так вот на первом все работает как и должно, а во втором случае нет. Скрипты сделаны два раза, т.к. на первом кодировка UTF-8, а на втором 1251, т.е. между собой они не связаны.

Ладно, лирика все это :) буду искать и пробовать.

А может помочь вызов скрипта так
include_once(' ');
Или это от редиректа не поможет?
Ответить


Re: Ограничение на скорость выдачи страниц одному пользователю

Точно! Вызывается там в коде redirect!
Код: Выделить всё
if (isset($HTTP_GET_VARS['sid']))
            {
               redirect("viewtopic.$phpEx?sid=$session_id&" . POST_POST_URL . "=$post_id#$post_id");
            }
            else
            {
               redirect("viewtopic.$phpEx?" . POST_POST_URL . "=$post_id#$post_id");
            }

Теперь если можно подробно объясните :) я в Вашем скрипте делаю функцию с названием с большой буквы, а в основном коде нужно менять название? PHP регистрочувствителен?
Ответить


Connie писал(а):У Вас на первой станице:

Код: Выделить всё
$ad_Robots_IP = array(
'Aport' =&gt; array(

Я понял!
Это просто браузер так отображает, чтобы нарисовать нужную скобку, а не воспринимать её как часть тэга.
Странно, что код у Вас так копировался...

Connie писал(а):Вот тут

Код: Выделить всё
while (false !== ($ad_FName = readdir($ad_dir)))
{

верно сравнение записно? Не следует писать != :roll:

Да, записано верно.

Connie писал(а):То есть мне нужно в коде скрипта добавлять

Да, функцию нужно добавить в модуль. И все редиректы, которые делаются скриптами, нужно переписать на вызов этой функции.

Добавлено спустя 6 минут 7 секунд:
Connie писал(а):Точно! Вызывается там в коде redirect!

Замечательно!
Только теперь в Вашем случае нужно сделать следующее: нужно найти, где описывается эта функция и сделать изменения в ней самой. Если это форум, то там все редиректы делаются при помощи неё. И тогда будет удобно её немного подправить, чтобы не лазить по всем скриптам и не переделывать на свою функцию.
Выложите сюда эту функцию целиком, а я её подправлю под наш редирект.
Ответить


Re: Ограничение на скорость выдачи страниц одному пользователю

Выложите сюда эту функцию целиком, а я её подправлю под наш редирект.
Ну вот, а я сам хотел потренироваться ))))))))))))))
Я так понимаю в коде Вашего скрипта нужно ввести какой то флаг, а уже в функции редиректа проверять установлен ли он и только тогда писать во временный каталог, иначе получится, что туда при любом редиректе будут сбрасываться ip пользователей, ведь эта функция используется многими скриптами форума. Вот как ее проверять, что бы в случае, если она не определена, не возникало исключения? Вот этого я не знаю.

Вот функция:
Код: Выделить всё
function redirect($url)
{
   global $db, $board_config;

   if (!empty($db))
   {
      $db->sql_close();
   }

   if (strstr(urldecode($url), "\n") || strstr(urldecode($url), "\r"))
   {
      message_die(GENERAL_ERROR, 'Tried to redirect to potentially insecure url.');
   }

   $server_protocol = ($board_config['cookie_secure']) ? 'https://' : 'http://';
   $server_name = preg_replace('#^\/?(.*?)\/?$#', '\1', trim($board_config['server_name']));
   $server_port = ($board_config['server_port'] <> 80) ? ':' . trim($board_config['server_port']) : '';
   $script_name = preg_replace('#^\/?(.*?)\/?$#', '\1', trim($board_config['script_path']));
   $script_name = ($script_name == '') ? $script_name : '/' . $script_name;
   $url = preg_replace('#^\/?(.*?)\/?$#', '/\1', trim($url));
       
        //Я так понимаю, что тут нужно делать вставку

   // Redirect via an HTML form for PITA webservers
   if (@preg_match('/Microsoft|WebSTAR|Xitami/', getenv('SERVER_SOFTWARE')))
   {
      header('Refresh: 0; URL=' . $server_protocol . $server_name . $server_port . $script_name . $url);
      echo '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head><meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"><meta http-equiv="refresh" content="0; url=' . $server_protocol . $server_name . $server_port . $script_name . $url . '"><title>Redirect</title></head><body><div align="center">If your browser does not support meta redirection please click <a href="' . $server_protocol . $server_name . $server_port . $script_name . $url . '">HERE</a> to be redirected</div></body></html>';
      exit;
   }

   // Behave as per HTTP/1.1 spec for others
   header('Location: ' . $server_protocol . $server_name . $server_port . $script_name . $url);
   exit;
}
Ответить


Да, Вы правы. Именно в это место нужно вставить:
Код: Выделить всё
$f_name = $_SERVER['DOCUMENT_ROOT'] . 'tmp_dir/r' . $_SERVER['REMOTE_ADDR'];
$f = fopen($f_name, 'w');
fclose($f);

Connie писал(а):Я так понимаю в коде Вашего скрипта нужно ввести какой то флаг, а уже в функции редиректа проверять установлен ли он и только тогда писать во временный каталог

Как раз наоборот. При редиректе флаг (файл специального имени) устанавливается, а при работе скрипта он проверяется. И если он стоит, то скрипт пропускает этого пользователя.
Ответить


Пред.След.

Вернуться в PHP-скрипты



Кто сейчас на сайте

Зарегистрированные пользователи: Bing [Bot], Yandex [bot]