2013-06-24 15 views
0

Я нашел этот сценарийPHP улучшение защиты от наводнений сценарий

Quick and easy flood protection?

и я включил его в функцию.

Отлично работает по большей части. Время от времени я вижу ошибку:

[<a href='function.unlink'>function.unlink</a>]: No such file or directory 

в строке:

else if ($diff>3600) { unlink($path); } // If first request was more than 1 hour, new ip file

Очевидно, что некоторые файлы IP по какой-то причине становятся удалены?

Я попытался найти логическую ошибку, но я не очень хорош в этом. Может быть, кто-то может помочь.

Функция:

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 = '/home/czivbaby/valuemarket.gr/ip-sec/'; // 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); 
      } 
     } 
    } 
} 

ответ

0

Сервер обрабатывает два (или больше) запросов в то же время от того же самого клиента, и сценарий, кажется, не правильно обращаться с этим (вполне нормальное) положение. Веб-браузеры загружают несколько объектов с сервера параллельно, чтобы ускорить просмотр. Вполне вероятно, что время от времени браузер выполняет два запроса, которые затем заканчиваются параллельно, так что две копии этого скрипта оказываются при одном и том же вызове unlink() примерно в одно и то же время. Успешно удаляется файл, а другой - сообщение об ошибке.

Даже если ваш сервер имеет один процессор, операционная система будет с удовольствием обеспечивать многозадачность путем переключения контекста между несколькими процессами PHP, которые одновременно выполняют один и тот же PHP-скрипт для одного и того же IP-адреса клиента.

Для блокировки файла при работе с ним сценарий должен, вероятно, использовать блокировку файлов (http://php.net/manual/en/function.flock.php). Или просто игнорируйте ошибку unlink() (помещая @ перед отключением), но могут возникнуть другие проблемы параллелизма.

Сценарий должен:

  1. Открыть файл для чтения и записи с помощью $f = fopen($filename, 'r+');
  2. блокировки открытого файла, используя дескриптор файла. Вызов flock($f, LOCK_EX) будет блокировать и ждать, если какой-либо другой процесс уже заблокирован.
  3. Прочитать содержимое файла.
  4. Решите, что делать (приращивание счетчика, отказ от обслуживания).
  5. fseek($f, 0, SEEK_SET) в начало файла, ftruncate($f, 0), чтобы сделать его пустым и переписать содержимое файла, если необходимо или unlink() если необходимо.
  6. Закройте дескриптор файла с fclose ($ F), который также снимает блокировку на нем и позволяет другой процесс переходите к шагу 3.

картина одинакова для всех языков программирования.

+0

Ошибка отображается случайным образом. Если бы это была проблема с запросами, то в течение 5000 ударов это выглядело бы точно. Также, имитируя поток из моего компьютера, он не вызывает ошибку. – user2517612

+0

Если у вас мало трафика (5000 ударов не так много, если это не произойдет за короткое время), вы можете увидеть ошибку довольно редко, в зависимости от настройки вашего веб-сервера. Вы можете воспроизвести его, создавая параллельные запросы. Запускайте * несколько * копий небольшого скрипта одновременно: 'while:; do curl -s -o/dev/null http://example.com/script.php; done' – oh7lzb

+0

Я попытаюсь реализовать некоторую блокировку файла. Я не могу получить доступ к OP сценария, он мог бы добавить его легко. – user2517612