Страницы

Поиск по вопросам

вторник, 31 декабря 2019 г.

Защита от частых перезагрузок страницы

#php


Итак. Если например за 1 минуту с одного IP идёт 4 запроса, то мы блокируем доступ
к сайту на 15 минут. Реализовать я это пытался на файлах, то есть записываем каждый
раз в файл:
ip|timestamp|0
Далее проверять, если последний timestamp меньше, чем 3 секунды, то добавлять 

ip|timestamp|1|ip|timestamp|2|ip|timestamp|3

и т.д. Если превысило 5, то блокируем доступ к сайту на 10 минут например. 
Но пришла в голову плохая мысль, ведь пользователей много в онлайне и файл заполнится
до такой степени, что всему придёт конец. Сессии и cookie предлагать не стоит, так
как запрос со стороннего сервера. Или вовсе моё мнение ошибочно?    


Ответы

Ответ 1



А может не изобретать велосипед, а взять готовый и погнуть немного? Называется велосипед fail2ban.

Ответ 2



Для nginx модуль LimitReq, который по дефолту включен при компиляциии. Для Apache не так просто, вопрос на SO. Предлагают mod_evasive и mod_cband, а также в общем случае mod_limitipconn, mod_bw, mod_bwshare.

Ответ 3



Если вЕлик необходим, я тут набросал. Расходы времени и размеров можно сократить, организовав сам файл правильно. К примеру, переводить ip в бинарный вид: function ip2bin($ip) { $ip = explode('.', $ip); return chr($ip[0]).chr($ip[1]).chr($ip[2]).chr($ip[3]); } // и наоборот: function bin2ip($bin) { return ord($bin[0]).'.'.ord($bin[1]).'.'.ord($bin[2]).'.'.ord($bin[3]); } Тогда точно будешь уверен: данные об IP занимают 4 байта. В идеале и всю остальную инфу тоже нужно хранить так же. Но я для примера сделал так: // понадобится для нормирования чисел в «обычном виде»: function norm_len($s, $len = 11, $fill = '0') { if (strlen($s) >= $len) return $s; else { $fill = $fill[0]; return str_repeat($fill, $len - strlen($s)).$s; } } $f = fopen('iptest.bin', "w"); $counter = 0; // заполняем нашу базу случайными значениями: for ($counter = 0; $counter < 200000; $counter++) { // случайный IP: $ip = rand(0,255).'.'.rand(0,255).'.'.rand(0,255).'.'.rand(0,255); // просто, чтоб знать, какой IP найдется — для теста: $count = $counter+1; $timestamp = time(); // приводим к нужному виду: $ip = ip2bin($ip); //$ip = norm_len($ip, 4, ' '); // 4 байта; приводить не требуется $count = norm_len($count, 10, '0'); // 10 байт $timestamp = norm_len($timestamp, 11, '0'); // 11 байт // теперь мы уверены, что размер каждой записи составляет // 4 + 10 + 11 = 25 (байт) fwrite($f, $ip.$count.$timestamp, 25); } fclose($f); die('Filled. Last IP: '.bin2ip($ip)); Файлик заполнен. Пробуем искать: $f = fopen('iptest.bin', "r"); $current_ip = '146.115.156.250'; $ip = NULL; fseek($f, 0); echo 'Searching for "'. $current_ip .'"...'."\n"; $current_ip = ip2bin($current_ip); // приводим к бинарному виду $length = filesize('iptest.bin'); //while (!feof($f)) // не работает почему-то.. while (ftell($f) < $length) { $ip = fread($f, 4); if ($ip === $current_ip) { echo '+ ' . bin2ip($ip).' <- that`s it!'."\n"; $count = intval(fread($f, 10)); $timestamp = intval(fread($f, 11)); echo ' Timestamp: '. $timestamp .' ('. date('Y-m-d H:i:s', $timestamp) .')'."\n"; echo ' Count: '. $count ."\n"; //break; // не будем выходить при первом совпадении } else { //echo '- ' . bin2ip($ip) ."\n"; fseek($f, 21, SEEK_CUR); // всегда точно знаем, на сколько нужно сместиться } } fclose($f); Функцию norm_len нужно доделать еще для отрицательных чисел (ну или учитывать "00000-23464" уже при «распаковке»). Хранилище изобретено. Чтение файла достаточно быстрое, но все же будет сильно нагружать систему при большом количестве клиентов. Этот метод нужно использовать для выявления злоумышленника, после чего информацию о бане записывать в сессию, чтобы не гонять постоянно винт по этому файлу. Я еще подумаю, как сделать асинхронный поиск по файлу (к примеру, составлять список задач, и производить поиск не чаще 1 раза в 10 секунд, после чего сохранять вердикт в самопальную сессию). Это позволит снизить нагрузку при чтении, но может дать плохишу дополнительные 10 секунд. Осталось только придумать, какие данные там лучше всего хранить, чтобы выявлять плохишей. Мой личный вывод: лучше не трогать похапе и прибегнуть к использованию готовых, более продвинутых решений.

Ответ 4



Imho вариант с базой данных гораздо лучше варианта с файлом. Когда 100 потоков начнут писать в файл, то могут возникать коллизии или блокировки на запись. Никаких группировок и сортировок при работе с файлом не удастся нормально сделать. Когда-нибудь файл вырастет до такой степени, что защита сама сайт и положит. Вообще, смысла "Защищаться от частых перезагрузок страницы" нет. Если пользователь хочет перезагружать - пусть перезагружает. Делайте кеширование, если так нужно. Другое дело - защищать свой контент от неопознанных ботов-воришек. Т.е. вы можете сделать такую штуку, чтобы поиграться. Но профита она не даст. Готовое решение уже советовали - fail2ban. Блокировки ip будут на уровне iptables, а не кода приложения. Блокировки на основе лог-файлов сервера.

Комментариев нет:

Отправить комментарий