首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >php防洪改进脚本

php防洪改进脚本
EN

Stack Overflow用户
提问于 2013-06-24 19:49:39
回答 1查看 1.2K关注 0票数 0

我找到了这个剧本

Quick and easy flood protection?

我把它变成了一种功能。

大部分情况下效果很好。我不时地看到一个错误:

代码语言:javascript
复制
[<a href='function.unlink'>function.unlink</a>]: No such file or directory 

排成一行:

如果($diff>3600) { unlink($path);} //如果第一次请求超过1小时,则为新ip文件。

显然有些IP文件因为某种原因被删除了?

我试图找出逻辑上的错误,但我一点也不擅长。也许有人能帮忙。

职能:

代码语言:javascript
复制
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);
            }
        }
    }
} 
EN

回答 1

Stack Overflow用户

发布于 2013-06-24 20:22:56

您的服务器同时处理来自同一个客户端的两个(或多个)请求,并且脚本似乎没有正确地处理这种(完全正常)情况。Web浏览器并行地从服务器上下载多个对象,以加快浏览速度。很可能,浏览器时不时地执行两个请求,然后并行执行,这样脚本的两个副本就会在大致相同的时间内在同一个unlink()调用中结束。一个成功地删除了文件,另一个给出了错误消息。

即使您的服务器只有一个CPU,操作系统也将通过多个PHP进程之间的上下文切换来愉快地提供多任务处理,这些进程同时对同一个客户端IP地址执行相同的PHP脚本。

脚本应该在处理文件时使用文件锁定(http://php.net/manual/en/function.flock.php)来锁定文件。或者忽略unlink()错误(在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
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/17283858

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档