快速简便的防洪?

时间:2010-06-11 22:23:48

标签: php protection spam

我有一个网站,用户使用AJAX将消息提交到名为like.php的文件。在此文件中,用户消息将提交给数据库,然后将链接发送回用户。在我的Javascript代码中,我禁用了用户在提交AJAX请求时键入的文本框。

唯一的问题是,恶意用户可以不断向like.php发送POST请求并充斥我的数据库。所以我想实施简单的防洪保护。

我真的不想让另一个数据库表记录用户IP等麻烦...好像他们充斥着我的网站会有很多数据库读/写速度慢下来。我想过使用会话,比如有一个包含时间戳的会话,每次将数据发送到like.php时都会检查,如果当前时间在时间​​戳之前让他们将数据添加到数据库,否则发送一个错误并阻止他们。如果允许他们在数据库中输入内容,请使用新的时间戳更新其会话。

你怎么看?这是最好的方法吗?还是有更容易的选择?

感谢您的帮助。 :)

7 个答案:

答案 0 :(得分: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

答案 1 :(得分:5)

使用令牌。您生成令牌并将其添加到发起请求的页面。在like.php中,您验证请求是否包含有效令牌,这意味着它来自您的网页而不是外部直接POST。

答案 2 :(得分:3)

另一种方法是使用jQuery将隐藏的表单输入写入页面(调用like.php)。机器人将不会使用javascript,因此您的隐藏表单字段将不存在。

检查隐藏字段(为其指定值和名称),如果存在,则使用请求命中数据库。

另一种方式;将隐藏元素编码到页面中(<input style='display:none;' name='nospam' value='' />)。机器人将自动填充表单中的每个字段,因此您只需检查该字段是否已填充 - 用户无法看到它,因此如果您有内容,则您知道它是机器人。

使用jQuery设置样式(display:none;)...再次,机器人不会看到jQuery,所以它会认为这是一个合法的表单输入。

您可能需要为用户指定“此页面需要运行javascript”通知。一些替代建议。毕竟 - 你说'简单';)

答案 3 :(得分: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 *");
}

答案 4 :(得分:2)

我做了一个脚本来处理核心请求(没有会话请求或其他没有调用核心的请求)。如果你看看谷歌你会发现脚本/类会因为每次高负荷而杀死你的服务器。事实上,许多人使用SESSION,也许还有SQL /数据库,这将使您获得作为服务器杀手的防洪保护。此外,SESSION需要Cookie(或GET SID),以便您可以轻松地操作SESSION获取新的SESSION ID。

我的功能是基于文本的,并且操作简单。不好的是,你可能不得不使用CronJob来不时删除ips。与其他脚本相比,它的速度提高了10倍(并且比会话节省了更多)。

我不知道它是否真的有用。 ;) 您可能希望将rpm值更改为less或/以及200 req。我的设置禁止机器人在&lt; = 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个用户。我问谷歌和测试(例如)http://technitip.net/simple-php-flood-protection-class

所以我不知道那里有什么应该是简单的?您一次有大约3个SQL请求,如:

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

它可能提供更多功能,但所有合法用户都不会使用servertime。 ;)

答案 5 :(得分:0)

  

我想过使用会话,比如   有一个包含a的会话   每次都要检查的时间戳   他们将数据发送到like.php

这不会阻止机器人,因为他们可以接收和发送用户所做的相同的cookie。

您应该让用户登录到这样的系统。似乎值得保护访问。您还可以考虑限制每个IP每分钟的帖子,但多个机器人仍然可以发送许多垃圾邮件。

如果您不想实施登录,那么许多网站都会使用captcha来尝试减少此类尝试。

http://www.phpcaptcha.org/

答案 6 :(得分: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秒他会被拦住!