HTTP_FloodControl Class
Introduction
The HTTP_FloodControl package can be used to detect and protect a Web site
from attempts to flood it with too many requests. It also allows to protect the
site from automatic downloading many pages or files from the same IP address,
session ID or other unique identifier.
The detection of flood is determine according to a set of parameters indicating
the maximal allowed number of requests for the certain time interval. It is
possible to set several parameters at once in order to perform more effective
protection.
The package uses various storage containers (regular files, DB, MDB, MDB2) to
handle counter logs.
License Agreement
This library is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the Free
Software Foundation; either version 2.1 of the License, or (at your option)
any later version.
This library is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License
for more details.
Credits
Written by Vagharshak Tozalakyan © 2007
This package is based on
FloodBlocker
class previously created by author for PHP Classes
project.
Requirements and Dependences
- PHP version 5
- PEAR::Exception
- PEAR::DB (optional)
- PEAR::MDB (optional)
- PEAR::MDB2 (optional)
See downloads section for direct links to
dependent PEAR packages.
How To Use
Using HTTP_FloodControl is quite simple. Normally it is necessary to set an
appropriate storage container, define limits and call the check() method,
which returns false if flooding attempt detected based on supplied limits
and unique identifier.
<?php require_once 'HTTP/FloodControl.php'; try { $ip = HTTP_FloodControl::getUserIP(); } catch (HTTP_FloodControl_Exception $e) { die($e); } try { $fc =& new HTTP_FloodControl(); $fc->setContainer('File', '/home/user1/fc_logs'); $limits = array ( 10 => 10, // maximum 10 requests in 10 seconds 60 => 30, // maximum 30 requests in 60 seconds 300 => 50, // maximum 50 requests in 300 seconds 3600 => 200 // maximum 200 requests in 3600 seconds ); if (!$fc->check($limits, $ip)) { die('Too many requests. Please try later.'); } } catch (HTTP_FloodControl_Exception $e) { die($e); } echo 'Hello world!'; ?>
To run the above example it is necessary to have a directory /home/user1/fc_logs
with writing permissions for the script.
Normally flood protection code should be written at the top of the script. The
code may exist in a separate PHP file which is possible to include on desired
pages of a site.
Using File Container
In order to use the File container it is necessary to create a separate directory
for counter logs storage. The script must have permission to write files in that
directory.
Logs directory may be created somewhere outside the root of documents or an
.htaccess file may be used inside that directory to prevent illegal access.
Please note, that the garbage collector will delete all files from the logs
directory except those started with a dot.
<?php require_once 'HTTP/FloodControl.php'; try { $ip = HTTP_FloodControl::getUserIP(); } catch (HTTP_FloodControl_Exception $e) { die($e); } try { $fc =& new HTTP_FloodControl(); $fc->setContainer('File', '/home/user1/fc_logs'); // Probability of garbage collection, optional $fc->setProbability(75); // Maximum lifetime of counter logs, optional $fc->setLifetime(7200); $limits = array ( 3600 => 100 // maximum 100 requests in 3600 seconds ); if (!$fc->check($limits, $ip)) { die('Too many requests. Please try later.'); } } catch (HTTP_FloodControl_Exception $e) { die($e); } echo 'Hello world!'; ?>
Using Database Containers
First of all it is necessary to create a database table with the following
structure:
CREATE TABLE fc_logs ( unique_id varchar(32) NOT NULL, data text NOT NULL, access int UNSIGNED NOT NULL, PRIMARY KEY (unique_id) )
In the following example MDB2 container is being used. DB and MDB containers
may be used the same way, only MDB2 should be changed to the name of appropriate
container.
<?php require_once 'HTTP/FloodControl.php'; try { $ip = HTTP_FloodControl::getUserIP(); } catch (HTTP_FloodControl_Exception $e) { die($e); } try { $fc =& new HTTP_FloodControl(); $fc->setContainer('MDB2', array( 'dsn' => 'mysql://username:password@localhost/dbname', 'table' => 'fc_logs', 'autooptimize' => true )); $limits = array ( 300 => 10 // maximum 10 requests in 300 seconds ); if (!$fc->check($limits, $ip)) { die('Too many requests. Please try later.'); } } catch (HTTP_FloodControl_Exception $e) { die($e); } echo 'Hello world!'; ?>
To use an existed database connection, a valid connection handle may be passed
to the setContainer() instead of DSN string.
<?php require_once 'MDB2.php'; require_once 'HTTP/FloodControl.php'; $dsn = 'mysql://username:password@localhost/dbname'; $dbh =& MDB2::connect($dsn); if (PEAR::isError($dbh)) { die(getMessage($dbh)); } try { $ip = HTTP_FloodControl::getUserIP(); } catch (HTTP_FloodControl_Exception $e) { die($e); } try { $fc =& new HTTP_FloodControl(); $fc->setContainer('MDB2', array( 'dsn' => $dbh, 'table' => 'fc_logs', 'autooptimize' => true )); $limits = array ( 300 => 10 // maximum 10 requests in 300 seconds ); if (!$fc->check($limits, $ip)) { die('Too many requests. Please try later.'); } } catch (HTTP_FloodControl_Exception $e) { die($e); } echo 'Hello world!'; ?>
Applying Multiple Limits
The check() method may be called more than one time. For example, in the
following listing two different limit arrays are used for subnet address and
current session identifier.
<?php session_start(); require_once 'HTTP/FloodControl.php'; try { $fc =& new HTTP_FloodControl(); $result = $fc->setContainer('MDB2', array( 'dsn' => 'mysql://root@localhost/test', 'table' => 'flood_logs', 'autooptimize' => true )); // Optional probability of garbage collection $fc->setProbability(50); $ip_parts = explode('.', $_SERVER['REMOTE_ADDR']); $subnet_addr = $ip_parts[0] . $ip_parts[1]; if (!$fc->check(array(300 => 10, 3600 => 100), $subnet_addr)) { die('Too many requests from the same subnet.'); } elseif (!$fc->check(array(600 => 15), session_id())) { die('Too many requests with the same session ID.'); } } catch (HTTP_FloodControl_Exception $e) { die($e); } echo 'Hello world!'; ?>
Incremental Locking
Let's assume the following code is used for flood control:
<?php ... $fc->setIncrementalLock(true); if (!$fc->check(array(60 => 30))) { die('Flood detected!'); } ... ?>
If there was more than 30 requests during 1 minute, the access will be blocked
for the next 1 minute, so the user can access the page only after 1 minute from
the latest over-request. If the user try to access the locked page, the access
will be blocked for 1 minute again (incrementally).
Incremental locking is set to false by default.
Downloads
HTTP_FloodControl-0.1.1.tgz
Here are some external packages you may download:
PEAR: PEAR base classes
DB: database abstraction layer
MDB: database abstraction layer
MDB2: database abstraction layer
MDB2_Driver_fbsql: fbsql MDB2 driver
MDB2_Driver_ibase: ibase MDB2 driver
MDB2_Driver_mssql: mssql MDB2 driver
MDB2_Driver_mysql: mysql MDB2 driver
MDB2_Driver_mysqli: mysqli MDB2 driver
MDB2_Driver_oci8: oci8 MDB2 driver
MDB2_Driver_pgsql: pgsql MDB2 driver
MDB2_Driver_querysim: querysim MDB2 driver
MDB2_Driver_sqlite: sqlite MDB2 driver
|