php flood protection improvement script

浪尽此生 提交于 2019-12-12 01:05:43

问题


I have found this script

Quick and easy flood protection?

and I have turned it into a function.

Works great for the most part. From time to time I see an error:

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

in line:

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

Apparently some IP files for some reason are getting deleted ?

I have tried to find the logic error, but I'm not good at all at that. Maybe somebody could help.

The function:

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);
            }
        }
    }
} 

回答1:


Your server is processing two (or more) requests at the same time from the same client, and the script does not seem to handle this (completely normal) situation correctly. Web browsers download multiple objects from a server in parallel in order to speed up browsing. It's quite likely that, every now and then, a browser does two requests which then end up executing in parallel so that two copies of that script end up at the same unlink() call at roughly the same time. One succeeds in deleting the file, and the other one gives the error message.

Even if your server has a single CPU, the operating system will be happily providing multitasking by context switching between multiple PHP processes which are executing the same PHP script at the same time for the same client IP address.

The script should probably use file locking (http://php.net/manual/en/function.flock.php) to lock the file while working on it. Or simply ignore the unlink() error (by placing a @ in front of the unlink), but other concurrency problems are likely to come up.

The script should:

  1. Open the file for reading and writing using $f = fopen($filename, 'r+');
  2. Lock the opened file using the file handle. The flock($f, LOCK_EX) call will block and wait if some other process already has a lock.
  3. Read file contents.
  4. Decide what to do (increment counter, refuse to service).
  5. fseek($f, 0, SEEK_SET) to beginning of file, ftruncate($f, 0) to make it empty and rewrite the file contents if necessary or unlink() the file if necessary.
  6. Close the file handle with fclose($f), which also releases the lock on it and lets another process continue with step 3.

The pattern is same for all programming languages.



来源:https://stackoverflow.com/questions/17283858/php-flood-protection-improvement-script

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!