There may be situations when you want to throttle the amount of requests a specific user or IP address can make to your website. This works great if you are using Apache as a reverse proxy for security, availability or performance reasons. Back in the Apache 1.x days there was a module called mod_dosevasive that did just the trick. Unfortunately it did not work as well in Apache 2.x.
A much better solution is to use a module called mod_security. mod_security allows you to write sophisticated, stateful rules and take action based on particular conditions. Using mod_security you can do a lot more than DOS evasive maneuvers. You can filter for XSS, SQL Injection, Mail Header Injection and lots more. It uses Perl regular expressions for the win.
In preliminary tests the filter does not block search engine spiders (at least not the ones that count).
If you are using FreeBSD ports, you’ll also need to change the default:
SecRuleEngine DetectionOnly
to:
SecRuleEngine On
and add:
SecDataDir /tmp
in /usr/local/etc/apache22/Includes/mod_security2/modsecurity_crs_10_config.conf
The following can be used in a VirtualHost directive, or included directly in httpd.conf. It can be easily tailored to suit your needs:
# Ignoring media files, count requests made in past 10 seconds. SecRule REQUEST_BASENAME "!(css|doc|flv|gif|ico|jpg|js|png|swf|pdf)$" "phase:1,nolog,pass,initcol:ip=%{REMOTE_ADDR},setvar:ip.requests=+1" # This is where every other example online goes wrong. We want the var to expire and leave it # alone. If we combine this with the increments in the rule above, the timer never expires unless # there are absolutely no requests for 10 seconds. SecRule ip:requests "@le 2" "phase:1,nolog,expirevar:ip.requests=10" # if there were more than 20 requests in 10 seconds for this IP # set var block to 1 (expires in 30 seconds) and increase var blocks by one (expires in 5 minutes) SecRule ip:requests "@ge 20" "phase:1,pass,nolog,setvar:ip.block=1,expirevar:ip.block=30,setvar:ip.blocks=+1,setvar:ip.requests=0,expirevar:ip.blocks=300" # If user was blocked more than 5 times (var blocks>5), log and return http 403 SecRule ip:blocks "@ge 5" "phase:1,deny,log,logdata:'req/sec: %{ip.requests}, blocks: %{ip.blocks}',status:403" # if user is blocked (var block=1), log and return http 403 SecRule ip:block "@eq 1" "phase:1,deny,log,logdata:'req/sec: %{ip.requests}, blocks: %{ip.blocks}',status:403" # 403 is some static page or message ErrorDocument 403 "<html><body><h2>Too many requests.</h2></body></html>" |
The above blocks users who send more than 20 requests in a 10 second period. They will be blocked for 30seconds unless this has be a frequent occurrence. If they were blocked more than five times within five minutes they will be blocked for five minutes.