Synook

Flood control in PHP

Many web applications allow users to add content, such as posting on forums or commenting on blogs. Here is a way to make sure no-one floods the input by repeating the same action over and over again in a short space of time.

For this, we’ll be using the clients’ IP (v4) addresses to determine who they are. First, we’ll set up a small table (fcontrol) which will store IP addresses and the last time they did certain actions (in this case, commenting on a blog and registering for an account) as UNIX timestamps.

mysql_query("CREATE TABLE fcontrol (
  client_ip VARCHAR(16) NOT NULL PRIMARY KEY, 
  register_time INT(24) NOT NULL,
  comment_time INT(24) NOT NULL
)");

Now, currently (before implementing this flood control system) our register processing code looks like this (sort of):

if ($registering && validates()) { 
  perform_register();
}

Now, we will create a new function, fcontrol($field, $delay), where $field is the action we are checking (i.e. "register" or "comment") and $delay is the required delay between actions, and check it along with our validation.

function fcontrol($field, $delay) { /*...*/ }
//...
if ($registering && validates()) {
  if (fcontrol("register", 86400)) { 
    perform_register();
  }
}

All following code will go inside the fcontrol() function.

Inside this function, we first need to check whether the client’s IP address exists in our flood-control table. To do this, we retrieve the client’s IP address through the $_SERVER[‘REMOTE_ADDR’] superglobal index, and match it in our database by selecting it. If it returns rows, the client has already performed an action (but it may be before the cut-off time). If not, they pass, because they haven’t completed the action ever, but we must also update their time.

$ip = $_SERVER['REMOTE_ADDR'];
$now = time();
$result = mysql_query("SELECT {$field}_time AS time FROM fcontrol WHERE client_ip = '$ip'");
if (!mysql_num_rows($result)) {
  mysql_query("INSERT INTO fcontrol (client_ip, {$field}_time) VALUES ('$ip', $now)");
  return true;
}

Now, if the client’s IP is already in our database, we then have to check whether the last time they performed this action was within the delay. If it is, then we return false, or else we update the time and return true.

$time = mysql_fetch_assoc($result);
$time = $time['time'];
if ($now - $time > $delay) {
  mysql_query("UPDATE fcontrol SET {$field}_time = $now WHERE client_ip = '$ip'"); 
  return true;
} else return false;

This function can then be re-used for anything you want to impose a time delay on. Just don’t forget to include a column in the fcontrol table for each action!