2012-01-29 56 views
2

我想知道如何防止單個IP地址使用過多的帶寬並快速訪問我的網頁。也就是說,檢查用戶的IP地址(我認爲$_SERVER['REMOTE_ADDR']?),檢查該用戶的最新訪問,計算時間差,並在間隔很短的情況下對頁面進行塊渲染。我對嗎?如果是這樣,我怎麼能做到這一點,而不需要在服務器上消耗太多的資源和/或時間?如果有數據庫方法,是不是會造成太多的鎖?基於IP的頁面訪問次數限制

+4

如果一個用戶真的想用這種方式攻擊你的網站,一個簡單的PHP IP塊不會阻止他們。特別是因爲請求已經存在於PHP中,並且已經使用了資源。 – animuson 2012-01-29 22:07:28

+0

你錯過了最重要的東西,服務器是你的,例如VPS,專用的或者你有權訪問防火牆和/或你可以獲得apt-get包嗎?或只管理共享主機 – 2012-01-29 22:14:40

+0

服務器是專用的。 – 2012-01-30 02:26:56

回答

4

mod_bandwidth的阿帕奇可以控制某些IP

BandWidth <domain|ip|all> <rate>

http://mansurovs.com/tech/apache-bandwidth-throttling

+2

您的權利,如果請求點擊php,那麼攻擊者已經贏了。 – 2012-01-29 22:24:54

+0

我喜歡在訪問PHP之前限制客戶端,但我不想僅限制他們使用的帶寬。假設有一個客戶端像一個簡單的多線程爬行程序一樣實現,並且正在下載我網站中的所有頁面。誰可以阻止它? – 2012-01-30 12:02:11

5

最好的方法取決於你試圖阻止誰。如果真正的用戶經常刷新頁面,那麼(a)是否真的想阻止他們 - 他們是您的用戶!?,並且(b)您可以使用基於會話的方法來避免數據庫命中。如果它是殭屍,那麼你不能依賴會話(因爲它們可能不會發送會話頭,或者它們可能是當前的,但它們是會繞過它的惡意機器人)。

如果是正版用戶,那麼假設你有設置會話cookie OK,你會想是這樣的:

<?php 
$min_seconds_between_refreshes = 3; 

session_start(); 

if(array_key_exists('last_access', $_SESSION) && time()-$min_seconds_between_refreshes <= $_SESSION['last_access']) { 
    // The user has been here at least $min_seconds_between_refreshes seconds ago - block them 
    exit('You are refreshing too quickly, please wait a few seconds and try again.'); 
} 
// Record now as their last access time 
$_SESSION['last_access'] = time(); 
?> 

如果它是機器人,那麼你很可能會實現一個基於數據庫的解決方案具有類似的邏輯。

實際上,這兩種情況下的正確解決方案可能都是應用程序服務器前面的高速緩存代理服務器。這將減少主應用程序服務器的負載,並且意味着您不必擔心如此多的情況。

+0

我想阻止* evil *用戶/機器人誰可能想要使用我的服務器的所有帶寬與一個簡單的爬蟲。數據庫方法的開銷是多少? – 2012-01-30 11:59:15

+0

好的,所以會話方法將不起作用。數據庫將會有一些開銷,但是您可能每次請求都會多次訪問數據庫?一個額外的不會有太大的區別。但是,這將是一張醜陋的桌子,您必須定期寫一些東西來清除舊訪客。 @eugene的解決方案對我來說似乎最好。 – Howard 2012-01-30 12:42:15

1

這是洪水檢測代碼,使用Memcache。如果用戶在一分鐘內超過50次訪問,他就會癱瘓300秒。遠程地址用於識別客戶端。

<?php 

$limit = 50; 
$seconds = 60; 
$block_for_seconds = 300; 

$status = 'OK'; 

$memcache = new Memcache; 
$memcache->connect('localhost', 11211); 

$ip = $_SERVER['REMOTE_ADDR']; 

$r = $memcache->get($ip, array('c', 't')); 

$c = 1; // count 
$init_time = time(); 
if($r) { 
    $s = $r[3]; // status 
    $c = $r[0]+1; 
    $init_time = $r[1]; 
    if($s == 'TOO_MANY_REQUESTS') { 
    $d = time()-$r[1]; // time since block 
    if($block_for_seconds-$d > 0) { // still blocked 
     die('Flood detected!! You are going to wait '.($block_for_seconds-$d).' and try again.'); 
    } else { // block is over 
     $status = 'OK'; 
     $init_time = time(); 
     $c = 0; 
    } 
    } 

    $new_time = time(); 
    if($c > $limit) { // check if happened within a minute 
    $time_elapsed = $new_time - $init_time; 
    if($time_elapsed < $seconds) { 
     $status = 'TOO_MANY_REQUESTS'; 
    } 
    print "time elapsed: $time_elapsed, count:$c"; 
    $c = 0; 
    $init_time = time(); 
    } 
} 
print_r($r); 
$memcache->set($ip, array($c, $init_time, $new_time, $status)); 
?>