CGI::Application::Plugin::RateLimit.3pm

Langue: en

Version: 2006-05-04 (debian - 07/07/09)

Section: 3 (Bibliothèques de fonctions)

NAME

CGI::Application::Plugin::RateLimit - limits runmode call rate per user

SYNOPSIS

   use CGI::Application::Plugin::RateLimit;
 
   sub setup {
     ...
 
     # call this in your setup routine to set
     my $rate_limit = $self->rate_limit();
 
     # set the database handle to use
     $rate_limit->dbh($dbh);
 
     # set the table name to use for storing hits, the default is
     # 'rate_limit_hits'
     $rate_limit->table('rate_limit_hits');
 
     # keep people from calling 'send' more often than 5 times in 10
     # minutes and 'list' more often than once every 5 seconds.
     $rate_limit->protected_modes(send => {timeframe => '10m',
                                           max_hits  => 5
                                          },
                                  list => {timeframe => '5s',
                                           max_hits  => 1
                                          });
 
     # you can also protect abstract actions, for example to prevent a
     # flood of failed logins
     $rate_limit->protected_actions(failed_login => {timeframe => '10s',
                                                     max_hits  => 2
                                                    });
 
     # call this runmode when a violation is detected
     $rate_limit->violation_mode('too_fast_buddy');
 
     # or, run this callback
     $rate_limit->violation_callback(sub { die(...) });
 
     # override the default identity function
     # ($ENV{REMOTE_USER} || $ENV{REMOTE_IP})
     $rate_limit->identity_callback(sub { ... });
   }
 
   # record a hit for an action (not needed for run-modes which are
   # handled automatically)
   $rate_limit->record_hit(action => 'failed_login');
 
   # check for a violation on an action and handle
   return $self->slow_down_buddy
     if( $rate_limit->check_violation(action => 'failed_login') );
 
   # revoke the most recent hit for this user, preventing it from
   # counting towards a violation
   $rate_limit->revoke_hit();
 
   # examine the violation in violation_mode or violation_callback:
   $mode   = $rate_limit->violated_mode;
   $action = $rate_limit->violated_action;
   $limits = $rate_limit->violated_limits;
 
 

DESCRIPTION

This module provides protection against a user calling a runmode too frequently. A typical use-case might be a contact form that sends email. You'd like to allow your users to send you messages, but thousands of messages from a single user would be a problem.

This module works by maintaining a database of hits to protected runmodes. It then checks this database to determine if a new hit should be allowed based on past activity by the user. The user's identity is, by default, tied to login (via REMOTE_USER) or IP address (via REMOTE_IP) if login info is not available. You may provide your own identity function via the identity_callback() method.

To use this module you must create a table in your database with the following schema (using MySQL-syntax, although other DBs may work as well with minor alterations):

   CREATE TABLE rate_limit_hits (
      user_id   VARCHAR(255)      NOT NULL,
      action    VARCHAR(255)      NOT NULL,
      timestamp UNSIGNED INTEGER  NOT NULL,
      INDEX (user_id, action, timestamp)
   );
 
 

You may feel free to vary the storage-type and size of user_id and action to match your usage. For example, if your identity_callback() always returns an integer you could make user_id an integer column.

This table should be periodically cleared of old data. Anything older than the maximum timeframe being used can be safely deleted.

IMPORTANT NOTE: The protection offered by this module is not perfect. Identifying a user on the internet is very hard and a sophisticated attacker can work around these checks, by switching IPs or automating login creation.

INTERFACE

The object returned from calling "$self->rate_limit" on your CGI::App object supports the following method calls:

dbh

    $rate_limit->dbh($dbh);
 
 

Call this to set the database handle the object should use. Must be set in setup().

table

    $rate_limit->table('some_table_name');
 
 

Call this to determine the table to be used to store and lookup hits. The default is 'rate_limit_hits' if not set. See the DESCRIPTION section for the required table schema.

protected_modes

     $rate_limit->protected_modes(send => {timeframe => '10m',
                                           max_hits  => 5
                                          },
                                  list => {timeframe => '5s',
                                           max_hits  => 1
                                          });
 
 

Takes a list of key-value pairs describing the modes to protect. Keys are names of run-modes. Values are hashes with the following keys:

   timeframe - the timeframe to be considered for violations.  Values
   must be numbers followed by either 's' for seconds, 'm' for minutes
   or 'h' for hours.
 
   max_hits - how many hits to allow in the specified timeframe before
   triggering a violation.
 
 

protected_actions

     $rate_limit->protected_actions(failed_login => {timeframe => '10s',
                                                     max_hits  => 2
                                                    });
 
 

Specifies non-run-mode actions to protect. These are arbitrary keys you can use with record_hit() and check_violation(). Takes the same data-structure as protected_modes().

violation_mode

   $rate_limit->violation_mode('too_fast_buddy');
 
 

Call to set a run-mode to call when a violation is triggered. Either this or violation_callback must be set.

violation_callback

     $rate_limit->violation_callback(sub { ... });
 
 

Callback to call when a violation is detected. Should either throw an exception or return the run-mode to run. Called with the CGI::App object as its sole parameter.

identity_callback

     $rate_limit->identity_callback(sub { ... });
 
 

Call this to provide a customized mechanism for determining the identity of the user. The default is:

   sub { $ENV{REMOTE_USER} || $ENV{REMOTE_IP} }
 
 

You might consider adding in session-ID or a hook to your authentication system if it doesn't use REMOTE_USER. Whatever you write should return a single scalar which is expected to be unique to each user.

record_hit

   $rate_limit->record_hit(action => 'failed_login');
 
 

Record a hit for an arbitrary action. This is not needed for run-mode protection. Takes the action name as an argument, which must match an action registered with protected_actions().

check_violation

   return $self->slow_down_buddy
     if( $rate_limit->check_violation(action => 'failed_login') );
 
 

Checks for a violation of a protected action. This is not needed for run-mode protection. Takes the action name as an argument, which must match an action registered with protected_actions().

Returns 1 if a violation took place, 0 otherwise.

revoke_hit

   $rate_limit->revoke_hit();
 
 

Revokes the last hit for this user. You might use this to prevent validation errors from counting against a user, for example.

violated_mode

   $mode = $rate_limit->violated_mode;
 
 

Returns the mode for the last violation, or undef if an action caused the violation.

violated_action

   $mode = $rate_limit->violated_action;
 
 

Returns the action for the last violation, or undef if an action caused the violation.

violated_limits

   $limits = $rate_limit->violated_limits;
 
 

Returns the hash-ref passed to protected_actions() or protected_modes() for the violated mode/action.

DATABASE SUPPORT

I've tested this module with MySQL and SQLite. I think it's likely to work with many other databases - please let me know if you try one.

SUPPORT

Please send questions and suggestions about this module to the CGI::Application mailing-list. To join the mailing list, simply send a blank message to:
   cgiapp-subscribe@lists.erlbaum.net
 
 

VERSION CONTROL

This module is in a public Subversion repository at SourceForge here:
    https://svn.sourceforge.net/svnroot/html-template/trunk/CGI-Application-Plugin-RateLimit
 
 

BUGS

I know of no bugs. If you find one, let me know by filing a report on http://rt.cpan.org. Failing that, you can email me at sam@tregar.com. Please include the version of the module you're using and small test case demonstrating the problem.

AUTHOR

Sam Tregar, sam@plusthree.com Copyright (C) 2006 by Sam Tregar

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.6 or, at your option, any later version of Perl 5 you may have available.