2010-06-11 2 views
7

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

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

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

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

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

+0

Спам-бот не будет распознавать cookie сеанса, поэтому, возможно, вообще не будет использовать сеанс. Он также может злонамеренно изменять идентификатор сеанса каждый раз, оставляя вас без возможности отслеживать его. Вероятно, вы не сможете обойти какой-либо IP-журнал. –

+0

Заставить пользователя войти в систему, чтобы отправить сообщение? (только принимайте 'like.php's' из подписанных учетных записей). Затем запретите пользователям отправлять слишком много за определенное время? – Wallter

+0

Спасибо за ваши предложения. Как я уже сказал в ответе на комментарии, мне действительно не хотелось бы, чтобы пользовательская система была главной точкой сайта - она ​​должна быть быстрой и без проблем. Мне нужно будет зайти в журнал IP-адресов :) –

ответ

5

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

+0

Затем, самое главное, вы аннулируете токен после его использования. Прикрепление токена к сеансу также добавляет еще один слой анти-бот-кода. (Примитивные боты не будут делать куки.) Теперь передовые боты все равно будут работать, но это будет больше работы для автора бота. – Charles

+0

Это на самом деле отлично. Но как я буду генерировать токен? Просто хэш из случайного числа или что-то еще? А также как «like.php» знает, какой токен действителен или нет? Должен ли я отправить его, хотя СЕССИЯ? :) Приветствия! –

+1

Что заставляет кого-то просматривать страницу один раз, получать токен, а затем автоматизировать запросы/POST с действительным токеном? – barfoon

0

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

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

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

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

http://www.phpcaptcha.org/

+0

Спасибо. Мне действительно не нравится идея пользовательской системы или CAPTCHA, поскольку главная цель сайта - быстро и быстро отправить сообщение. Похоже, мне придется идти с регистрацией IP-адресов в конце концов. –

7

Сессия является самой легкой для этого и имеет наименьшее издержки. Вы можете хранить два бита данных в сеансе, временную метку последнего сообщения, и 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 
+0

Этот вид работает ... но даже через 10 минут он все равно не позволит мне отправить комментарий (для чего я это использую). Как долго это 'MININTERVAL'? Кажется, дольше минуты, есть ли другой способ добавить одну минуту к '$ _SESSION ['last_post']'? – Nathan

+0

MININTERVAL - это заполнитель для ** количества секунд **, который вы хотите использовать. Если вы хотите установить 2-минутный интервал, измените строку на 'if (isset ($ _ SESSION ['ip']) && $ _SESSION ['last_post'] + 120

+0

О, я вижу, кричит. ЛОЛ. Благодаря! +1 – Nathan

3

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

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

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

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

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

2

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

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

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

<?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 пользователей. Я спрашиваю у Google и проверял (как пример) http://technitip.net/simple-php-flood-protection-class

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

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

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

+0

В сценарии есть условия гонки, когда одновременно запускаются несколько копий сценария и обрабатываются запросы с одного и того же IP-адреса. Счетчик иногда будет только увеличиваться один раз для двух или более запросов, а иногда вы получите сообщения об ошибках сбой unlink() (другой скрипт удалил его одновременно). [связанный вопрос] (http://stackoverflow.com/questions/17283858/php-flood-protection-improvement-script) – oh7lzb

3

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

<?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 *"); 
} 
0

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

$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 секунды он будет остановлен!

Смежные вопросы