0

再現できないエラーが発生します。

次のコードは、攻撃から保護するモジュールの一部です。この特定のスニペットは、特定のボット ユーザー エージェントからのヒット数を追跡​​しています。

何年も問題なく使用した後、突然エラーが発生しました。

不適切な数値が検出されました。

これは次の行で発生しています。

    $seconds = time() - $time;

$time の値は2016-10-02 19:33:42

関数 safefilename() は以下を返します。

Mozilla-5-0-compatible-spbot-5-0-3-http-OpenLinkProfiler-org-bot

読み書きされるファイルの名前は次のとおりです。

bot_2016-10-02--19-33-42_Mozilla-5-0-compatible-spbot-5-0-3-http-Open_104.131.179.5.log

方法論

以下のコードは、ボットを対象とし、ユーザー エージェントとファイルが作成された時刻に基づくファイル名に書き込みます。そのユーザー エージェントが使用されるたびに、ファイルに "X" が追加されるので、そのエージェントが何回アクセスしたかを追跡できます。ボットが一定回数以上私を狙っている場合は、ブロックします。

以下のコードは、テストと本番環境で望ましい結果を生成します - もちろん、このエラーがスローされた場合を除きます。上記のファイルには 6 バイトが書き込まれているため、これまでに 5 回正常に読み書きされています。

PHP エラーは 06:37:04 に記録され、サーバー ログ ファイルには次のヒットが表示されます。

104.131.63.140 - - [10/Dec/2016:06:36:59 -0800] "GET /robots.txt HTTP/1.1" 301 257 "-" "Mozilla/5.0 (compatible; spbot/5.0.3; +http://OpenLinkProfiler.org/bot )"

104.131.63.140 - - [10/Dec/2016:06:36:59 -0800] "GET /robots.txt HTTP/1.1" 200 1460 "-" "Mozilla/5.0 (compatible; spbot/5.0.3; +http://OpenLinkProfiler.org/bot )"

104.131.63.140 - - [10/Dec/2016:06:37:04 -0800] "GET / HTTP/1.1" 403 937 "-" "Mozilla/5.0 (compatible; spbot/5.0.3; +http://OpenLinkProfiler.org/bot )"

104.131.63.140 - - [10/Dec/2016:06:37:05 -0800] "GET / HTTP/1.1" 301 247 "-" "Mozilla/5.0 (compatible; spbot/5.0.3; +http://OpenLinkProfiler.org/bot )"

PHP コード スタンドアロンで実行してテストできる次のコードを抽出しました。

// this is my site address
define("STATIC_SITE_ROOT", "http://static"); 

$agent = "Mozilla/5.0 (compatible; spbot/5.0.3; +http://OpenLinkProfiler.org/bot )";
$ip = '127.0.0.1';
$t = new test();
$t->testAgent($agent, $ip);

class test {
    public $agent;
    public $ip;
    public $maxbadpages = 100;

    function testAgent($agent, $ip){
        $this->agent = $agent;
        $this->ip = $ip;

        if (strlen($badbot = $this->badbot($this->agent)) > 0){
            $new = FALSE;
            $path = $_SERVER['DOCUMENT_ROOT'] . "/logs";
            // $filename = "bot-" . time() . "-" . safefilename(substr($this->agent, 0, 50));
            $safefilename = safefilename(substr($this->agent, 0, 50));
            $filename = "bot_" . date("Y-m-d--H-i-s") . "_" . $safefilename . "_" . $this->ip . ".log";
            $filter = $safefilename;
            $afiles = getDirArray($path, $filter);
            if (count($afiles) > 0){
                // bot file already exists
                $filename = $afiles[0];     
            } else {
                // add time to filename if crating new file
                $new = TRUE;
            }
            $fullfilename = "$path/$filename";

            // log a counter (# bytes in file)
            file_put_contents($fullfilename, "X", FILE_APPEND);

            // number of hits == size of file
            $size = filesize($fullfilename);

            // count hits to determine if block via htaccess
            // if > # entries in log from a useragent, ban it
            if ($size > $this->maxbadpages){
                $this->blockagent($this->agent, $this->ip, "> $this->maxbadpages hits");
            } elseif (! $new) {
                // test for hits per second
                $blockagent = FALSE;
                $parts = explode("_", $filename);
                // 2nd part is the time
                // $time = strtotime($parts[1]);
                $parts2 = explode("--", $parts[1]);
                $time = $parts2[0] . " " . str_replace("-",":",$parts2[1]);
                // seconds is time elapsed
                $seconds = time() - $time;
                // check for various scenarios
                if ($size > $seconds * 2){
                    // more than average of 2 hits per second for any period
                    $blockagent = TRUE;
                    $reason = "$size (hits) > $seconds (seconds) * 2";
                }
                if ($seconds >= 10 && $size > $seconds * 1){
                    // more than 1 hit per second over 10 seconds
                    $blockagent = TRUE;
                    $reason = "$seconds (seconds) >= 10 && $size (hits) > $seconds (seconds) * 1";
                }
                if ($blockagent){
                    $this->blockagent($this->agent, $this->ip, $reason);            
                }
            }       
            $this->blockAccess("bad bot: ". $badbot);
        }
    }

    function blockAgent($message){
        die("Block Agent: " . $message);
    }

    function blockAccess($message){
        die("Block Access: " . $message);
    }

    function badbot($agent) {
        if (stripos($agent, "bot") !==FALSE){
            return "match 'bot' in agent: ($agent)";
        } elseif (stripos($agent, "spider") !==FALSE){
            return "match 'spider' in agent: ($agent)";
        } elseif (stripos($agent, "crawl") !==FALSE){
            return "match 'crawl' in agent: ($agent)";
        }
        $badbots = array(
        "007AC9",
        "2Bone",
        "404 Checker",
        "There are many more bad bots contained in this array...");

        foreach ($badbots as $bot) {
            //If the spider text is found in the current user agent, then return true
            if (stripos($agent, $bot) !== false){
                return "$bot ($agent)";
                return "match: $bot in agent: ($agent)";
            }
        }
        //If it gets this far then no bot was found!
        return "";
    }


}


function safefilename($string){
    // convert entities e.g. Á => Á
    $string = htmlentities($string, ENT_QUOTES, 'UTF-8');    

    // replace the entities with letter equivalents
    $string = preg_replace('~&([a-z]{1,2})(acute|cedil|circ|grave|lig|orn|ring|slash|th|tilde|uml);~i', '$1', $string);

    // return entities which did not have letter equivalents back to entities
    $string = html_entity_decode($string, ENT_QUOTES, 'UTF-8');

    // replace non valid chars with dash and multiple dashes with only one
    $string = preg_replace(array('~[^0-9a-z]~i', '~[ -]+~'), '-', $string);

    return trim($string, ' -');
}


function getDirArray($path = "./", $filter = ".*", $exclude = '', $sorted = true, $optfilter2 = '') {
    // for server directories, can't use the static url
    $path = str_replace(STATIC_SITE_ROOT, $_SERVER['DOCUMENT_ROOT'], $path);
    if (file_exists($path) == false) {
    if (mkdir($path, 0777, true) == false) {
        die($path);
        exit;
    }
    }

    $handle = opendir($path);
    $dir = array();
    while ($file = readdir($handle)) {
    if (is_file("$path/$file") && preg_match("/$filter/", $file) && (strlen($exclude) == 0 ? TRUE : !preg_match("/$exclude/", $file))) {
        if ($optfilter2 == '') {
        // No 2n filter
        $dir[] = $file;
        } else {
        $pos = strpos($file, $optfilter2);
        if ($pos === false) {
            // Not found
        } else {
            $dir[] = $file;
        }
        }
    }
    }
    closedir($handle);

    if ($sorted == true) {
    sort($dir);
    }

    return $dir;
}
4

1 に答える 1

3

問題は、UNIX タイムスタンプではなく日時文字列を使用していたことです。私のコメントで提案されているように、strtotime($time)これを修正するために使用する必要がありましたが、その理由を理解していないようです。

のドキュメントからtime

Unix エポック (1970 年 1 月 1 日 00:00:00 GMT) からの秒数で測定された現在の時刻を返します。

これは、実行時に GMT タイムゾーンで 1970 年の新年からの秒数 (整数time()) を返すことを意味します。

一方、文字列$timeである がありましたこの文字列は、秒数を表す整数ではなく、より読みやすい文字列です。今回はそうではありませんでしたが、UNIX タイムスタンプではなくこの文字列が必要な場合があります。

(整数)$timeから (文字列)を減算しようとしました。time()数字から文字を減算することはできないため、これは明らかに機能しません。そのため、エラーが発生していました。strtotimeあなたが提供したような文字列として日付を解析し、それを1970年の新年からの秒数の整数に変換できる関数です.

あなたのコメントでは、 で囲んだ後$time、結果としてstrtotime()5937340られると言いました。これは、現在の時刻と の秒単位の差$timeです。うまくいけば、これはあなたが探していたものです。約68.7日に相当します。これが期待した結果ではない場合は、さらにサポートを試みることができます。

クラスを使用して2つの日付文字列を互いに減算することもできDateTimeますが、私の意見では、あなたの状況ではより複雑で不要です. ただし、文字列の日付から整数の日付を減算することはできません。同じ型になるように変換する必要があります。うまくいけば、これを解決するのに役立ちました。

于 2016-12-10T20:49:33.437 に答える