Быстрая и простая защита от наводнений? - PullRequest
10 голосов
/ 12 июня 2010

У меня есть сайт, где пользователь отправляет сообщение с помощью AJAX в файл с именем like.php.В этом файле сообщение пользователя отправляется в базу данных, а затем отправляет ссылку обратно пользователю.В моем коде Javascript я отключил текстовое поле, которое вводит пользователь при отправке запроса AJAX.

Единственная проблема заключается в том, что злонамеренный пользователь может просто постоянно отправлять запросы POST на like.php и заполнять мою базу данных.Поэтому я хотел бы реализовать простую защиту от наводнений.

Я действительно не хочу, чтобы из-за другой таблицы базы данных регистрировались IP-адреса пользователей и тому подобное ... как будто они затопляют мой сайт, там будет много базы данныхчтение / запись замедляет его.Я думал об использовании сеансов, например, о том, чтобы иметь сеанс, который содержит временную метку, которая проверяется каждый раз, когда они отправляют данные в like.php, и, если текущее время находится перед временной меткой, пусть они добавляют данные в базу данных, в противном случае отправляют ошибку изаблокировать их.Если им разрешено что-то вводить в базу данных, обновите их сеанс с новой отметкой времени.

Как вы думаете?Будет ли это лучшим способом или есть более простые альтернативы?

Спасибо за любую помощь.:)

Ответы [ 7 ]

7 голосов
/ 12 июня 2010

Сеанс - самый простой способ сделать это, и он также требует наименьших накладных расходов.Вы можете сохранить два бита данных в сеансе, отметку времени последнего сообщения, и ip, с которого поступает сообщениеВот как вы проверяете легитимность:

session_start();
if(isset($_SESSION['ip']) && $_SESSION['last_post'] + MININTERVAL < time()) die('too early');

$_SESSION['last_post'] = time();
$_SESSION['ip'] = $_SERVER['REMOTE_ADDR'];
// store the message
5 голосов
/ 12 июня 2010

Используйте токен. Вы генерируете токен и добавляете его на страницу, с которой начинается запрос. В like.php вы проверяете, что запрос содержит действительный токен, что означает, что он приходит с вашей страницы, а не с внешнего POST-запроса напрямую.

3 голосов
/ 30 марта 2015

Вам не нужно просматривать весь файл записи. Вместо этого:

<?php
define("FLOODPOOL", ".");
define("FLOODPOOL_LIMIT", 30);
define("FLOODPOOL_DURATION", 60 * 60 * 24);
define("FLOODPOOL_AUTOCLEAN", true);

// Record and check flood.
// Return true for hit.
function floodpool_check($id){
    $fp = fopen(FLOODPOOL . DIRECTORY_SEPARATOR . 'fp_' . basename($id), 'a+');
    fwrite($fp, pack('L', time()));
    if(fseek($fp, -4 * FLOODPOOL_LIMIT, SEEK_END) === -1) {
        return false;
    }
    $time = reset(unpack('L', fread($fp, 4)));
    fclose($fp);
    if(time() - $time < FLOODPOOL_DURATION) {
        if(FLOODPOOL_AUTOCLEAN){
            @floodpool_clean();
        }
        return true;
    }
    return false;
}


// Clean the pool.
function floodpool_clean(){
    $handle = opendir(FLOODPOOL);
    while(false!==($entry=readdir($handle))){
        $filename = FLOODPOOL . DIRECTORY_SEPARATOR . $entry;
        if(time() - filectime($filename) > FLOODPOOL_DURATION && substr($entry, 0, 3) === 'fp_'){
            unlink($filename);
        }
    }
    closedir($handle);
}

Пример использования:

if(floodpool_check($_SERVER['REMOTE_ADDR'])){
    header("HTTP/1.1 429 Too Many Requests");
    exit("Hit some *");
}
3 голосов
/ 16 апреля 2012

Другой способ сделать это - написать скрытую форму ввода на страницу (которая вызывает like.php), используя jQuery.Бот не будет использовать javascript, поэтому ваше скрытое поле формы не будет существовать.

Проверьте наличие скрытого поля (присвойте ему значение и имя) и, если оно существует, попробуйте запросить базу данных с запросом..

Другой путь;закодируйте скрытый элемент на странице (<input style='display:none;' name='nospam' value='' />).Бот автоматически заполнит каждое поле в форме, поэтому вы просто проверяете, заполнено ли это поле - пользователь не может его увидеть, поэтому вы знаете, что это бот, если у вас есть содержимое.

Установитьстиль (display: none;) с использованием jQuery, но ... опять же, бот не увидит jQuery, поэтому он будет думать, что это допустимый ввод формы.

Возможно, вы захотите указать 'thisСтраница требует JavaScript для запуска «уведомления где-то для пользователя.Несколько альтернативных предложений.В конце концов - вы сказали «просто»;)

2 голосов
/ 04 мая 2012

Ну, я сделал скрипт для обработки его только для запросов ядра (без запросов сеанса или других запросов, которые не вызывают ядро).Если вы заглянете в Google, вы найдете скрипты / классы, которые будут убивать ваш сервер из-за высокой нагрузки каждый раз.Тот факт, что многие используют SESSIONs и, возможно, ТАКЖЕ SQL / Database, позволит вам получить защиту от наводнений как убийца сервера.Также тот факт, что сессиям нужен Cookie (или GET SID), чтобы вы могли легко управлять SESSION, чтобы получить новый идентификатор SESSION.

Моя функция основана на тексте и выполняет простую обработку.Плохо то, что вам, возможно, придется использовать CronJob, чтобы время от времени удалять ips.По сравнению с другими сценариями он примерно на 10 * быстрее (и экономит больше, чем сеансы).

Я не знаю, действительно ли он вообще полезен.;) Возможно, вы захотите изменить значение об / мин на меньшее или / и также 200 req.Моя настройка - запрет для бота, выполняющего интервальные запросы в течение <= 6 секунд. </p>

<?php
function ht_request_limiter() {
    if (!isset($_SERVER['REMOTE_ADDR'])) { return; } // Maybe its impossible, however we check it first
    if (empty($_SERVER['REMOTE_ADDR'])) { return; } // Maybe its impossible, however we check it first
    $path = '/your/path/ipsec/'; // I use a function to validate a path first and return if false...
    $path = $path.$_SERVER['REMOTE_ADDR'].'.txt'; // Real file path (filename = <ip>.txt)
    $now = time(); // Current timestamp
    if (!file_exists($path)) { // If first request or new request after 1 hour / 24 hour ban, new file with <timestamp>|<counter>
        if ($handle = fopen($path, 'w+')) {
            if (fwrite($handle, $now.'|0')) { chmod($path, 0700); } // Chmod to prevent access via web
            fclose($handle);
        }
    }
    else if (($content = file_get_contents($path)) !== false) { // Load existing file
        $content = explode('|',$content); // Create paraset [0] -> timestamp  [1] -> counter
        $diff = (int)$now-(int)$content[0]; // Time difference in seconds from first request to now
        if ($content[1] == 'ban') { // If [1] = ban we check if it was less than 24 hours and die if so
            if ($diff>86400) { unlink($path); } // 24 hours in seconds.. if more delete ip file
            else {
                header("HTTP/1.1 503 Service Unavailable");
                exit("Your IP is banned for 24 hours, because of too many requests.");
            }
        }
        else if ($diff>3600) { unlink($path); } // If first request was more than 1 hour, new ip file
        else {
            $current = ((int)$content[1])+1; // Counter + 1
            if ($current>200) { // We check rpm (request per minute) after 200 request to get a good ~value
                $rpm = ($current/($diff/60));
                if ($rpm>10) { // If there was more than 10 rpm -> ban (if you have a request all 5 secs. you will be banned after ~17 minutes)
                    if ($handle = fopen($path, 'w+')) {
                        fwrite($handle, $content[0].'|ban');
                        fclose($handle);
                        // Maybe you like to log the ip once -> die after next request
                    }
                    return;
                }
            }
            if ($handle = fopen($path, 'w+')) { // else write counter
                fwrite($handle, $content[0].'|'.$current .'');
                fclose($handle);
            }
        }
    }
}

Редактировать: Мой способ проверить время запроса был с помощью microtime и моделировать 10'000 пользователей.Спрашиваю гугл и проверял (как пример) http://technitip.net/simple-php-flood-protection-class

Так я не знаю, что там должно быть просто?У вас есть около 3 SQL-запросов одновременно, например:

$this -> user_in_db($ip))
$this->user_flooding($ip);
$this->remove_old_users();

Возможно, они предоставляют больше функций, но все законные пользователи используют серверное время даром.;)

0 голосов
/ 27 апреля 2016

Если вы хотите прекратить заполнение страницы поиска, вы можете попробовать это так:

$flood_protection_interval = 2;
session_start();
if(
    isset($_SESSION['ip']) && 
    $_SESSION['counter'] > 10 &&
    $_SESSION['last_post'] + $flood_protection_interval > time()
    ){
        // $_SESSION['counter'] = 0; // Use this if you want to reset counter
        die("<pre>\n\n\n\t<b>FLOOD PROTECTION</b>");
}
$_SESSION['counter']++;
$_SESSION['last_post'] = time();
$_SESSION['ip'] = $_SERVER['REMOTE_ADDR'];

Так что, если ваш посетитель ищет 10 раз, например, под. Через 2 секунды его остановят!

0 голосов
/ 12 июня 2010

Я думал об использовании сессий, как есть сеанс, который содержит отметка времени, которая проверяется каждый раз они отправляют данные в like.php

Это не остановит ботов, поскольку они могут получать и отправлять те же куки, что и пользователи.

У вас действительно должны быть пользователи, входящие в такую ​​систему. Кажется, стоит защищать доступ. Вы также можете ограничить количество сообщений в минуту на ip, но несколько ботов могут отправлять много спам-сообщений.

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

http://www.phpcaptcha.org/

...