Greetings all. I'm working on an application which will allow users
to send email to other people. For obvious reasons we'd like to make
it maximally difficult for spammers to use our app as a gateway. Take
a look at a part of my solution which I think might have use elsewhere
and let me know what you think. No code has been written yet, so
please feel free to be completely ruthless.
-sam
PS: Props to Perrin Harkins for helping with the design!
=head1 NAME
CGI::Application::Plugin::RateLimit - limits runmode call rate per
user
=head1 SYNOPSIS
package Some::App;
use base 'CGI::Application';
use CGI::Application::Plugin::RateLimit;
sub setup {
...
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
$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
});
# call this runmode when a violation is detected
$rate_limit->violation_mode('too_fast_buddy');
# or, run this callback
$rate_limit->violation_callback(sub { ... });
# override the default identity function
# ($ENV{REMOTE_USER} || $ENV{REMOTE_IP})
$rate_limit->identity_callback(sub { ... });
}
=head1 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):
CREATE TABLE rate_limit_hits (
user_id VARCHAR(255) NOT NULL,
module VARCHAR(255) NOT NULL,
run_mode VARCHAR(255) NOT NULL,
timestamp TIMESTAMP NOT NULL,
PRIMARY KEY (user_id, module, run_mode, timestamp)
);
You may feel free to vary the storage-type and size of user_id, module
and run_mode 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.
B<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.
=head1 INTERFACE
To be filled in before coding. For now, read the SYNOPSIS.
=head1 AUTHOR
Sam Tregar, [EMAIL PROTECTED]
=head1 COPYRIGHT AND LICENSE
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.
=cut
---------------------------------------------------------------------
Web Archive: http://www.mail-archive.com/[email protected]/
http://marc.theaimsgroup.com/?l=cgiapp&r=1&w=2
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]