Защита от спам ботов и DDOS атак
Как то вечером на одном из сайтов где множество товаров и большой выбор фильтров — пошла сильная нагрузка на ресурсы сервера. Немного изучив кто и что это, пришел к следующему выводу.
Встречался с подобными нагрузками и переборами всего, что есть на сайте где то в 2017 и 2020 году, так, что небольшой скриптик остался где-то.
Проблема в следующем:
- Хостинг объявил, что нагрузка на базу данных выросла примерно в 100 раз.
- Сайт отказывался работать и уходил в Ошибку 503
- В поддержке сказали что очень медленные запросы в базу данных и то что они ничего сделать не могут.
Решено было искать причину, так как до этого все работало норм и нагрузка не превышала лимиты даже при большом трафике.
Что было сделано:
- Первым делом зашел в админку сайта и перевел его в режим обслуживания, ошибки пропали так как все кроме админа видели заглушки страниц. А у администратора все страницы открывались и работали в штатном режиме.
- Далее зашел через putty на сервер и решил посмотреть что да как:
Узнать какие процессы и кто их создает на сервере:
1 | ps aux |
Увидел что большинство самых трудоресурсных процессов вызвано с левых IP адресов.
Просканировал айпишники, штук так 40 были с bc.googleusercontent.com и большинство начинаются с 35., 45., 46.,5. и т.д.
И все эти злокачественные IP вели на провайдера — Google LLC. Сначала подумал, что бот, а потом вспомнил, что можно было когда то с помощью гугла положить сайт, но это уже другая история:)
Да и еще: Чтобы понять, почему порождаются много процессов, нужно во время самой ошибки сделать трассировку системных процессов с помощью утилиты strace. Для этого после запуска ps aux запускаем следующую команду с указанием PID
1 | strace -ttfp PID |
Далее начал поиски того, что когда то делал, и нашел две версии.
Версия №1: сделать ссылку на страницы с пикселем для того, чтобы бот перешел по ней и попал в ловушку-бан.
Размещаем где то в конце странице, допустим какую то ссылку с картинкой.
1 2 | <a href="bot_ban/block_bot.php"> <img src="/bot_ban/pixel.gif" alt="" width="1" height="1"></a> |
Далее создаем в корне сайта папку bot_ban и помещаем туда картинку 1х1 px и создаем файлик block_bot.php
Внутри файла block_bot.php вставляем следующий код:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | <?php if (!empty($_SERVER['HTTP_CLIENT_IP'])) { $ip = $_SERVER['HTTP_CLIENT_IP']; } elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { $ip = $_SERVER['HTTP_X_FORWARDED_FOR']; } else { $ip = $_SERVER['REMOTE_ADDR']; } $bot = $_SERVER['HTTP_USER_AGENT']; if (strstr($_SERVER['HTTP_USER_AGENT'], 'Yandex')) { $bot = 'Yandex'; } elseif (strstr($_SERVER['HTTP_USER_AGENT'], 'Google')) { $bot = 'Google'; } elseif (strstr($_SERVER['HTTP_USER_AGENT'], 'Mail.Ru')) { $bot = 'Mail'; } elseif (strstr($_SERVER['HTTP_USER_AGENT'], 'StackRambler')) { $bot = 'Rambler'; } elseif (strstr($_SERVER['HTTP_USER_AGENT'], 'bing')) { $bot = 'Bing'; } elseif (strstr($_SERVER['HTTP_USER_AGENT'], 'Yahoo')) { $bot = 'Yahoo'; } if ($bot != 'Yandex' and $bot != 'Google' and $bot != 'Mail' and $bot != 'Rambler' and $bot != 'Bing' and $bot != 'Yahoo') { echo '<html><body><h1>Ошибка 404</h1>'; echo '<p><a href="https://адрес-вашего-сайта/index.php">вернуться на главную страницу</a></p>'; if (phpversion() >= "4.2.0") { extract($_SERVER); } $bad_bot = 0; /* Создаем в этой же папке пустой файл и называем его bot_block.dat. */ /* Далее смотрим, имеется ли такой же IP в базе, если нет то заносим его в список, так как простые люди просто так не найдут эту ссылку в один пиксель*/ $file_name = "bot_block.dat"; $fp = fopen($file_name, "r") or die("Ошибка файла<br>"); while ($line = fgets($fp, 255)) { $u = explode(" ", $line); if (preg_match("/" . $u[0] . "/", $REMOTE_ADDR)) { $bad_bot++; } } fclose($fp); if ($bad_bot == 0) { $tmestamp = time(); $datum = date("H:i:s d.m.Y", $tmestamp); mail("ваша@почта", "Атака ботов на вашсайт", "Бот с IP $REMOTE_ADDR пришел с $REQUEST_URI; Проверить расположение https://2ip.ru/info/$REMOTE_ADDR; User-agent: $HTTP_USER_AGENT; Со страницы: $HTTP_REFERER; Время блокировки: $datum "); $fp = fopen($file_name, 'a+'); fwrite($fp, "$REMOTE_ADDR $datum $REQUEST_URI $HTTP_REFERER $HTTP_USER_AGENT\r\n"); fclose($fp); }else{ $tmestamp = time(); $datum = date("H:i:s d.m.Y", $tmestamp); mail("ваша@почта", "Атака ботов на вашсайт", "Бот с IP $REMOTE_ADDR пришел с $REQUEST_URI; Проверить расположение https://2ip.ru/info/$REMOTE_ADDR; User-agent: $HTTP_USER_AGENT; Со страницы: $HTTP_REFERER; Время блокировки: $datum "); $fp = fopen($file_name, 'a+'); fwrite($fp, "$REMOTE_ADDR $datum $REQUEST_URI $HTTP_REFERER $HTTP_USER_AGENT\r\n"); fclose($fp); } echo '</body></html>'; } |
Так вот этот метод не сработал, так как этот типо гуглбот не переходил по ссылкам на сайте, а заранее по подготовленному списку урл переходил на страницу где есть фильтры и самая большая нагрузка на базу данных.
Версия №2: более продвинутый блокировщик
Было принято решение — сделать немного другую защиту от бота. В шаблоне сайта, в самом низу, вставил php код:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 | <?php if (!empty($_SERVER['HTTP_CLIENT_IP'])) { $ip = $_SERVER['HTTP_CLIENT_IP']; } elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { $ip = $_SERVER['HTTP_X_FORWARDED_FOR']; } else { $ip = $_SERVER['REMOTE_ADDR']; } $bot = $_SERVER['HTTP_USER_AGENT']; if (strstr($_SERVER['HTTP_USER_AGENT'], 'Yandex')) { $bot = 'Yandex'; } elseif (strstr($_SERVER['HTTP_USER_AGENT'], 'Google')) { $bot = 'Google'; } elseif (strstr($_SERVER['HTTP_USER_AGENT'], 'Yahoo')) { $bot = 'Yahoo'; } elseif (strstr($_SERVER['HTTP_USER_AGENT'], 'Mail')) { $bot = 'Mail'; } elseif (strstr($_SERVER['HTTP_USER_AGENT'], 'Bing')) { $bot = 'Bing'; } if ($bot != 'Bing' and $bot != 'Yandex' and $bot != 'Google' and $bot != 'Yahoo' and $bot != 'Mail') { $config['time'] = 3; /*Время обращения к скрипту, после которого будет(возможно) выдан бан.*/ $config['countaban'] = 5; /*количество возможных нарушений времени обращения, такая возможность введена для более детально точного отделения пользователей от атакующих ботов.*/ $config['directory'] = 'bot_ban/_temp'; /*временна директория, должна существовать, и иметь права на запись.*/ $config['checkmask'] = 'check'; /*маска для проверки, не стоит трогать.*/ $config['banmask'] = 'ban'; /*маска для бана, не стоит трогать.*/ /*Команды после нарушения времени и кол-ва обращений. Используйте константу [IP] - IP Бота.*/ /* iptables -A INPUT -m string --algo bm --string "bc.googleusercontent.com" -j DROP */ /* которые запускаются после того как превышено кол-во попыток */ $config['commands'][] = 'iptables -A ISPMGR -s [IP] -j DROP'; $config['commands'][] = 'iptables -I INPUT -s [IP] -j DROP'; /*Сообщение для пользователя. HTML Работает.*/ $config['message'] = "Сервер перегружен, пожалуйста обновите страницу"; $ip = $_SERVER['REMOTE_ADDR']; $useragent = $_SERVER['HTTP_USER_AGENT']; if (!is_dir($config['directory'])) { exit("Temp directory not found."); } if (!is_writable($config['directory'])) { exit("Temp directory is not writable."); } if (file_exists($config['directory'] . "/" . $config['checkmask'] . $ip)) { $time = filemtime($config['directory'] . "/" . $config['checkmask'] . $ip); $f = fopen($config['directory'] . "/" . $config['checkmask'] . $ip, 'w'); fclose($f); if ($time >= time() - $config['time']) { if (file_exists($config['directory'] . "/" . $config['banmask'] . $ip)) { $count = file_get_contents($config['directory'] . "/" . $config['banmask'] . $ip); if ($count >= $config['countaban']) { for ($i = 0; $i <= count($config['commands']) - 1; $i++) { @system(str_replace("[IP]", $ip, $config['commands'][$i])); } } else { $time = filemtime($config['directory'] . "/" . $config['banmask'] . $ip); if ($time >= time() - $config['time']) { $count++; $f = fopen($config['directory'] . "/" . $config['banmask'] . $ip, 'w'); fwrite($f, $count); fclose($f); } else { $f = fopen($config['directory'] . "/" . $config['banmask'] . $ip, 'w'); fwrite($f, "1"); fclose($f); } } } else { $f = fopen($config['directory'] . "/" . $config['banmask'] . $ip, 'w'); fwrite($f, "0"); fclose($f); } /* Также оставил возможность отправлять письма на почту с указанием что произошло*/ $tmestamp = time(); $datum = date("H:i:s d.m.Y", $tmestamp); $REMOTE_ADDR = $_SERVER['REMOTE_ADDR']; $REQUEST_URI = $_SERVER['REQUEST_URI']; $HTTP_USER_AGENT = $_SERVER['HTTP_USER_AGENT']; $HTTP_REFERER = $_SERVER['HTTP_REFERER']; mail("Ваша@почта", "DDOS атака на вашсайт", "Бот с IP $REMOTE_ADDR пришел с $REQUEST_URI; Проверить расположение https://2ip.ru/info/$REMOTE_ADDR; User-agent: $HTTP_USER_AGENT; Со страницы: $HTTP_REFERER; Время блокировки: $datum "); exit($config['message']); } } else { $f = fopen($config['directory'] . "/" . $config['checkmask'] . $ip, 'w'); /* если нужно чтобы что то записывалось в этот файл с проверкой */ /*$HTTP_REFERER = $_SERVER['HTTP_REFERER']; $HTTP_USER_AGENT = $_SERVER['HTTP_USER_AGENT']; fwrite($f, $HTTP_REFERER."=".$HTTP_USER_AGENT);*/ fclose($f); } } ?> |
Вроде после этого, бот немного приутих, так как большинство Ip попало в BAN
Да и еще, как то делал блокировку через htaccess, но только через IP, а вот через указание домена не получилось. Если кто знает, что да как, напишите буду благодарен!
1 2 3 4 5 6 | # Запрет googleusercontent переход на сайт <IfModule mod_rewrite.c> RewriteEngine On RewriteCond %{REMOTE_HOST} ^bc\.googleusercontent\.com$ RewriteRule .* - [F,L] </IfModule> |
🙂
PS: немного доработал вывод сообщения — добавил html в exit()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | $config['message'] = " <div id=\"bg_p\"> <div id=\"pop\"> <h1>Сервер перегружен, пожалуйста обновите страницу</h1> <p>Возможно вы сильно частно обновляете страницу?.</p> <p>Использовано [D] из 10 попыток.</p> </div> </div> <script type='text/javascript'>// <![CDATA[ document.getElementById('bg_popup').style.display='block'; // ]]></script> <style> #bg_p{ background-color: rgba(0, 0, 0, 0.9); display: none; position: fixed; z-index: 99999; top: 0; right: 0; bottom: 0; left: 0; } #pop { background: #fff; width: 520px; margin: 25% auto; text-align:center; padding: 5px 20px 13px 20px; border: 2px solid #ff6600; position: relative; -webkit-box-shadow: 0px 0px 20px #000; -moz-box-shadow: 0px 0px 20px #000; box-shadow: 0px 0px 20px #000; -webkit-border-radius: 15px; -moz-border-radius: 15px; border-radius: 15px; } </style>"; |
А также, в message дополнительно встроил счетчик попыток обновления страницы для наглядности.
1 | exit(str_replace("[D]", $count , $config['message'])); |




Блин походу не у одного у меня такая проблема
тоже гугл досит почерному а хост молчит
сделал все как написано по инструкции довольно просто — работает и это радует!
Да решение было давнишнее, но работало правда вот приходиться следить чтоб нормальные боты в блок не попали — нужно наблюдать !