Reputation: 15275
I wanted to know how can I prevent a single ip address from using too much bandwidth and rapidly access my webpages. That is, checking the user's ip address (I think $_SERVER['REMOTE_ADDR']
?), check for latest visit from this user, compute time difference and block rendering the page if the interval is short. Am I right? If so, how can I do it without consuming too much resource and/or time on the server? If there is a database approach, isn't it going cause too many locks?
Upvotes: 2
Views: 7156
Reputation: 460
Apache mod_bandwidth allows to control certain IPs
i.e. BandWidth <domain|ip|all> <rate>
https://httpd.apache.org/docs/trunk/mod/mod_ratelimit.html
Upvotes: 4
Reputation: 3895
The best approach depends on who you're trying to block. If it's genuine users who are constantly refreshing the page, then (a) do you really want to block them - they're your users!?, and (b) You can use a session-based approach to avoid DB hits. If it's bots then you can't rely on sessions (because they may not be sending the session headers, or they may be currently but are malicious bots who will get around it).
If it is genuine users, then assuming you're OK with setting session cookies, you'll want something like this:
<?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();
?>
If it's bots then you'll probably implement a database-based solution with similar logic.
In fact the correct solution in both cases is probably a caching proxy server in front of your application server. This will reduce load on your main app server and mean you won't have to worry so much about situations like this.
Upvotes: 6
Reputation: 11320
Here is the flood detection code that uses Memcache. If the user exceeds 50 visits within a minute, he's blokcked for 300 seconds. Remote address is used to identify the client.
<?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) );
?>
Upvotes: 2