Защита от DOS атак, Антифлуд, antiflood на php или Ограничение на скорость выдачи страниц одному пользователю
[h1]Защита от DOS атак, Антифлуд, (antiflood на php) или Ограничение на скорость выдачи страниц одному пользователю[/h1]
Иногда возможна перегрузка сервера при большом количестве запросов. И когда это происходит по вине одного злостного посетитела, или вируса, дос атаки, тогда нужно обезопасить себя.
Как?
Пишем скрипт, который проверяет время входа посетителя, и елси оно меньше установленной разницы, при следующем входе, тогда выдаем ошибку 503. "Временно не доступен".
Описание от автора скрипта: Случилось мне однажды столкнуться с ситуацией, когда мой хостер предъявил мне претензию о том, что мой акаунт создаёт непомерно большую нагрузку на MySQL-сервер. Посмотрев логи, я заметил, что такую нагрузку создают программы-качалки, которые копируют сайт целиком на локальный компьютер. Во время обращения к странице происходит несколько sql-запросов к базе данных. А если учесть, что эти программы готовы скачивать сразу несколько страниц с сайта, то получается, что в секунду идёт от 3 до 10 запросов. При такой «атаке» серверу действительно приходится не сладко. Решением я увидел ограничение доступа к сайту с одного ip-адреса чаще, чем один раз в 2 секунды. (Это значение можно регулировать). Проверка происходит без использования sql-сервера, поэтому идёт достаточно быстро. Здесь я предлагаю php-скрипт, реализующий такую проверку. Подключение этого модуля нужно вставлять в самом начале каждой страницы. Вторую версию скрипта породила ошибка, появляющаяся при одновременном обращении к файлу, хранящему информацию о последних посетителях. Во второй версии посещения записываются в виде файлов нулевой длины в специальную временную директорию. Кроме того, изменён способ учёта интервала: если раньше интервал считался от первого обращения, то теперь от последнего. Таким образом, если раньше при «бомбардировке» сайта раз в несколько секунд страница всё-таки отдавалась, то теперь нормальный показ возобновится лишь после того, как будет полностью выдержан установленный интервал времени. Третья версия скрипта включает в себя два коренных изменения. Во-первых, если посетитель продолжает атаковать ваш сайт, то через определённое количество запросов его ip-адрес будет внесёт в «чёрный список» файла .htaccess, и ему будет полностью закрыт доступ к сайту. Запрет через .htaccess практически не отнимает у процессора время. Во-вторых, проверка захода поисковых роботов теперь ведётся не по полю HTTP_USER_AGENT, а по ip-адресу. Для этого есть две причины. Первая из них — это то, что, например, поисковая система Aport не всегда подписывается. Наверное, это из-за того, что они боятся, что для их робота будут выдаваться другие страницы, нежели для посетителя-человека. Интересно, что более популярные поисковики не опускаются до такой паранойи. Вторая причина в том, что некоторые программы-качалки ухитряются выдавать себя за поисковых роботов. А вот это уже серьёзно. Поэтому было принято решение пропускать мимо этого скрипта все запросы с ip-адресов, принадлежащим компаниям-поисковикам, т.к. нет уверенности в точных адресах роботов и в том, что эти адреса не будут меняться.
Источник: http://avy.ru/ftopic1870.html
Описание от автора скрипта: Случилось мне однажды столкнуться с ситуацией, когда мой хостер предъявил мне претензию о том, что мой акаунт создаёт непомерно большую нагрузку на MySQL-сервер. Посмотрев логи, я заметил, что такую нагрузку создают программы-качалки, которые копируют сайт целиком на локальный компьютер. Во время обращения к странице происходит несколько sql-запросов к базе данных. А если учесть, что эти программы готовы скачивать сразу несколько страниц с сайта, то получается, что в секунду идёт от 3 до 10 запросов. При такой «атаке» серверу действительно приходится не сладко. Решением я увидел ограничение доступа к сайту с одного ip-адреса чаще, чем один раз в 2 секунды. (Это значение можно регулировать). Проверка происходит без использования sql-сервера, поэтому идёт достаточно быстро. Здесь я предлагаю php-скрипт, реализующий такую проверку. Подключение этого модуля нужно вставлять в самом начале каждой страницы. Вторую версию скрипта породила ошибка, появляющаяся при одновременном обращении к файлу, хранящему информацию о последних посетителях. Во второй версии посещения записываются в виде файлов нулевой длины в специальную временную директорию. Кроме того, изменён способ учёта интервала: если раньше интервал считался от первого обращения, то теперь от последнего. Таким образом, если раньше при «бомбардировке» сайта раз в несколько секунд страница всё-таки отдавалась, то теперь нормальный показ возобновится лишь после того, как будет полностью выдержан установленный интервал времени. Третья версия скрипта включает в себя два коренных изменения. Во-первых, если посетитель продолжает атаковать ваш сайт, то через определённое количество запросов его ip-адрес будет внесёт в «чёрный список» файла .htaccess, и ему будет полностью закрыт доступ к сайту. Запрет через .htaccess практически не отнимает у процессора время. Во-вторых, проверка захода поисковых роботов теперь ведётся не по полю HTTP_USER_AGENT, а по ip-адресу. Для этого есть две причины. Первая из них — это то, что, например, поисковая система Aport не всегда подписывается. Наверное, это из-за того, что они боятся, что для их робота будут выдаваться другие страницы, нежели для посетителя-человека. Интересно, что более популярные поисковики не опускаются до такой паранойи. Вторая причина в том, что некоторые программы-качалки ухитряются выдавать себя за поисковых роботов. А вот это уже серьёзно. Поэтому было принято решение пропускать мимо этого скрипта все запросы с ip-адресов, принадлежащим компаниям-поисковикам, т.к. нет уверенности в точных адресах роботов и в том, что эти адреса не будут меняться.
Code: php |
<?php /* *-------------------------------------------------------- * Модуль antiddos.php V3.1.3 Вт 16 Март 2010 * 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-адресов роботов; добавляем или * удаляем, что нужно. * 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) * 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) * 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) * 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) * LiveInternet - 88.212.202.0 - 88.212.202.63 (88.212.202.0/26) * Ip-адреса и маски к ним кодируются в виде строки из 4 символов * Это сделано из-за того, что невозможно в PHP использовать * стандартными (простыми) процедурами 32-битное целое число * без знака. А использование специальных библиотек усложнит * работу и сделает скрипт зависимым от этих библиотек. *--------------------------------------------------------------- */ $ad_Robots_IP = array( '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) ), '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) ), '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) ), '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) ), 'LiveInternet' => array( sprintf('%c%c%c%c', 88, 212, 202, 0), sprintf('%c%c%c%c', 255, 255, 255, 192) ), ) ; /* *---------------------------------------------------------- * Функция создаёт в указанной директории файл, начинающийся * с буквы a (для отличия от других возможных файлов) и * содержащий в имени ip-адрес клиента. * Если количество обращений подряд с данного адреса * превысило допустимый предел, адрес закрывается через файл * .htaccess * При желании может быть отослано письмо на специальный * адрес, в котором будет передана информация о действиях * с заблокированного адреса. *---------------------------------------------------------- */ function ad_WriteIP($counter) { $counter++; if ($counter > AD_TRYING) { //Баним ip-адрес $f = fopen($_SERVER['DOCUMENT_ROOT'] . '/.htaccess', 'a') ; fwrite($f, "ndeny from " . $_SERVER['REMOTE_ADDR'] ) ; fclose($f) ; // Открыть комментарии, если нужно уведомлять по почте /* //Получаем хост (в некоторых логах он может быть вместо ip $host = gethostbyaddr($_SERVER['REMOTE_ADDR'] ) ; $mess = 'Заблокирован адрес ' . $_SERVER['REMOTE_ADDR'] . ' (' . $host . " )nn"; //Выполняем запрос к логам. Нужно указать путь и имя лог-файла exec('cat /home/your_dir/logs/access_log | egrep '(' . str_replace('.', '.', $_SERVER['REMOTE_ADDR'] ) . ')|(' . 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' . $_SERVER['REMOTE_ADDR'] . '_' . $counter, 'w') ; fclose($f) ; } } /* *---------------------------------------------------- * Проверка на отношение ip-адреса к сетям поисковиков *---------------------------------------------------- */ $ad_IsRobot = false; $ad_IP = explode('.', $_SERVER['REMOTE_ADDR'] ) ; $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_match) { //Если на входящий адрес наложить маску операцией "и", //то он должен будет совпасть с начальным адресом сети. if (($ad_IPMatch & $ad_match[1] ) == $ad_match[0] ) { $ad_IsRobot = true; break; } } /* *--------------------------------------------------------- * Поисковые роботы не любят, когда к адресу страницы * добавляется переменная сессии. Поэтому, если на сайте * используются сессии, то их лучше включать, если агент - * не робот. * Если сессии не используются, то этот кусок можно убрать. *--------------------------------------------------------- */ /* if (!$ad_IsRobot) { session_start() ; } else { // Чтобы поисковый робот не сильно загружал сайт, //делаем ему задержку sleep(AD_DELAY) ; } */ 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 (ereg('^a[1-9]', $ad_FName)) { if (@ filemtime(AD_DIRNAME . '/' . $ad_FName) < $ad_forbid){ @ unlink(AD_DIRNAME . '/' . $ad_FName) ; } elseif (ereg('^a' . str_replace('.', '.', $_SERVER['REMOTE_ADDR'] ) . '_([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 > 0) { /* Если обращение было недавно, то выводим сообщение об ошибке */ header ('HTTP/1.0 503 Service Unavailable') ; header ('Status: 503 Service Unavailable') ; header ('Retry-After: ' . $ad_delay * 3) ; ?> <!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" /> </head> <body> <h1>Ошибка 503 (Service Unavailable)</h1> <p>Сервер не может в данный момент выдать запрашиваемую Вами страницу. Попробуйте вызвать эту страницу позже (клавиша F5).</p> </body> </html> <?php ad_WriteIP($ad_before_trying) ; // Перед выходом записываем ip exit; }else{ ad_WriteIP($ad_before_trying) ; } } ?> |