1RateLimit(3) User Contributed Perl Documentation RateLimit(3)
2
3
4
6 CGI::Application::Plugin::RateLimit - limits runmode call rate per user
7
9 use CGI::Application::Plugin::RateLimit;
10
11 sub setup {
12 ...
13
14 # call this in your setup routine to set
15 my $rate_limit = $self->rate_limit();
16
17 # set the database handle to use
18 $rate_limit->dbh($dbh);
19
20 # set the table name to use for storing hits, the default is
21 # 'rate_limit_hits'
22 $rate_limit->table('rate_limit_hits');
23
24 # keep people from calling 'send' more often than 5 times in 10
25 # minutes and 'list' more often than once every 5 seconds.
26 $rate_limit->protected_modes(send => {timeframe => '10m',
27 max_hits => 5
28 },
29 list => {timeframe => '5s',
30 max_hits => 1
31 });
32
33 # you can also protect abstract actions, for example to prevent a
34 # flood of failed logins
35 $rate_limit->protected_actions(failed_login => {timeframe => '10s',
36 max_hits => 2
37 });
38
39 # call this runmode when a violation is detected
40 $rate_limit->violation_mode('too_fast_buddy');
41
42 # or, run this callback
43 $rate_limit->violation_callback(sub { die(...) });
44
45 # override the default identity function
46 # ($ENV{REMOTE_USER} || $ENV{REMOTE_IP})
47 $rate_limit->identity_callback(sub { ... });
48 }
49
50 # record a hit for an action (not needed for run-modes which are
51 # handled automatically)
52 $rate_limit->record_hit(action => 'failed_login');
53
54 # check for a violation on an action and handle
55 return $self->slow_down_buddy
56 if( $rate_limit->check_violation(action => 'failed_login') );
57
58 # revoke the most recent hit for this user, preventing it from
59 # counting towards a violation
60 $rate_limit->revoke_hit();
61
62 # examine the violation in violation_mode or violation_callback:
63 $mode = $rate_limit->violated_mode;
64 $action = $rate_limit->violated_action;
65 $limits = $rate_limit->violated_limits;
66
68 This module provides protection against a user calling a runmode too
69 frequently. A typical use-case might be a contact form that sends
70 email. You'd like to allow your users to send you messages, but
71 thousands of messages from a single user would be a problem.
72
73 This module works by maintaining a database of hits to protected
74 runmodes. It then checks this database to determine if a new hit
75 should be allowed based on past activity by the user. The user's
76 identity is, by default, tied to login (via REMOTE_USER) or IP address
77 (via REMOTE_IP) if login info is not available. You may provide your
78 own identity function via the identity_callback() method.
79
80 To use this module you must create a table in your database with the
81 following schema (using MySQL-syntax, although other DBs may work as
82 well with minor alterations):
83
84 CREATE TABLE rate_limit_hits (
85 user_id VARCHAR(255) NOT NULL,
86 action VARCHAR(255) NOT NULL,
87 timestamp UNSIGNED INTEGER NOT NULL,
88 INDEX (user_id, action, timestamp)
89 );
90
91 You may feel free to vary the storage-type and size of user_id and
92 action to match your usage. For example, if your identity_callback()
93 always returns an integer you could make user_id an integer column.
94
95 This table should be periodically cleared of old data. Anything older
96 than the maximum timeframe being used can be safely deleted.
97
98 IMPORTANT NOTE: The protection offered by this module is not perfect.
99 Identifying a user on the internet is very hard and a sophisticated
100 attacker can work around these checks, by switching IPs or automating
101 login creation.
102
104 The object returned from calling "$self->rate_limit" on your CGI::App
105 object supports the following method calls:
106
107 dbh
108 $rate_limit->dbh($dbh);
109
110 Call this to set the database handle the object should use. Must be
111 set in setup().
112
113 table
114 $rate_limit->table('some_table_name');
115
116 Call this to determine the table to be used to store and lookup hits.
117 The default is 'rate_limit_hits' if not set. See the DESCRIPTION
118 section for the required table schema.
119
120 protected_modes
121 $rate_limit->protected_modes(send => {timeframe => '10m',
122 max_hits => 5
123 },
124 list => {timeframe => '5s',
125 max_hits => 1
126 });
127
128 Takes a list of key-value pairs describing the modes to protect. Keys
129 are names of run-modes. Values are hashes with the following keys:
130
131 timeframe - the timeframe to be considered for violations. Values
132 must be numbers followed by either 's' for seconds, 'm' for minutes
133 or 'h' for hours.
134
135 max_hits - how many hits to allow in the specified timeframe before
136 triggering a violation.
137
138 protected_actions
139 $rate_limit->protected_actions(failed_login => {timeframe => '10s',
140 max_hits => 2
141 });
142
143 Specifies non-run-mode actions to protect. These are arbitrary keys
144 you can use with record_hit() and check_violation(). Takes the same
145 data-structure as protected_modes().
146
147 violation_mode
148 $rate_limit->violation_mode('too_fast_buddy');
149
150 Call to set a run-mode to call when a violation is triggered. Either
151 this or violation_callback must be set.
152
153 violation_callback
154 $rate_limit->violation_callback(sub { ... });
155
156 Callback to call when a violation is detected. Should either throw an
157 exception or return the run-mode to run. Called with the CGI::App
158 object as its sole parameter.
159
160 identity_callback
161 $rate_limit->identity_callback(sub { ... });
162
163 Call this to provide a customized mechanism for determining the
164 identity of the user. The default is:
165
166 sub { $ENV{REMOTE_USER} || $ENV{REMOTE_IP} }
167
168 You might consider adding in session-ID or a hook to your
169 authentication system if it doesn't use REMOTE_USER. Whatever you
170 write should return a single scalar which is expected to be unique to
171 each user.
172
173 record_hit
174 $rate_limit->record_hit(action => 'failed_login');
175
176 Record a hit for an arbitrary action. This is not needed for run-mode
177 protection. Takes the action name as an argument, which must match an
178 action registered with protected_actions().
179
180 check_violation
181 return $self->slow_down_buddy
182 if( $rate_limit->check_violation(action => 'failed_login') );
183
184 Checks for a violation of a protected action. This is not needed for
185 run-mode protection. Takes the action name as an argument, which must
186 match an action registered with protected_actions().
187
188 Returns 1 if a violation took place, 0 otherwise.
189
190 revoke_hit
191 $rate_limit->revoke_hit();
192
193 Revokes the last hit for this user. You might use this to prevent
194 validation errors from counting against a user, for example.
195
196 violated_mode
197 $mode = $rate_limit->violated_mode;
198
199 Returns the mode for the last violation, or undef if an action caused
200 the violation.
201
202 violated_action
203 $mode = $rate_limit->violated_action;
204
205 Returns the action for the last violation, or undef if an action caused
206 the violation.
207
208 violated_limits
209 $limits = $rate_limit->violated_limits;
210
211 Returns the hash-ref passed to protected_actions() or protected_modes()
212 for the violated mode/action.
213
215 I've tested this module with MySQL and SQLite. I think it's likely to
216 work with many other databases - please let me know if you try one.
217
219 Please send questions and suggestions about this module to the
220 CGI::Application mailing-list. To join the mailing list, simply send a
221 blank message to:
222
223 cgiapp-subscribe@lists.erlbaum.net
224
226 This module is in a public Subversion repository at SourceForge here:
227
228 https://svn.sourceforge.net/svnroot/html-template/trunk/CGI-Application-Plugin-RateLimit
229
231 I know of no bugs. If you find one, let me know by filing a report on
232 http://rt.cpan.org. Failing that, you can email me at sam@tregar.com.
233 Please include the version of the module you're using and small test
234 case demonstrating the problem.
235
237 Sam Tregar, sam@plusthree.com
238
240 Copyright (C) 2006 by Sam Tregar
241
242 This library is free software; you can redistribute it and/or modify it
243 under the same terms as Perl itself, either Perl version 5.8.6 or, at
244 your option, any later version of Perl 5 you may have available.
245
246
247
248perl v5.36.0 2023-01-20 RateLimit(3)