Excessive_404_Blacklist
Problem this snippet solves:
This iRule will block ALL further site access to source IP addresses that exceed a certain number of HTTP requests to server resources that results in a 404 not found error. This script is useful for scenarioes where you have a system that contains fixed paths with files and want to keep people from brute-forcing those filenames.
By default, this will apply when a source IP gets 100 errors in a 10 minute period - and will block site access for another 10 minutes. This will slow down a brute force attack significantly and provide you with some log details you can use to troubleshoot.
This rule bases its logic on the incoming source IP, so if you have a large number of users coming through proxies you may want to consider setting a relatively high threshold. If you don't like using this with a source IP, it's possible to rewrite this logic to use a cookie instead, but most brute-force tools don't honor cookies -- source IP is still the most logical choice, even if not the most precise.
Code :
when RULE_INIT {
# excessive_404_blacklist
# v1.0 - Joel Moses
#
# Block all site access to source IP addresses that exceed a
# certain number of HTTP requests to non-existant objects within
# a fixed period of time. Useful for blocking harvesting
# activities (or at least slowing them down significantly).
#
# Use this iRule at your own risk. If your site has a propensity
# to throw off lots of 404 errors in normal operation, you may
# inadvertently find valid users blocked.
# Set the maximum number of 404 errors for a client IP in a
# certain period of time. The default setings are one hundred
# 404 errors in ten minutes. Following this, the client IP
# address will be blacklisted and an error page displayed for
# the next ten minutes.
set static::maxerrors 100
set static::maxperiod 600
set static::holdtime 600
# If for any reason you need to rid yourself of a
# blacklist, you can send the VIP running this iRule
# a query string parameter in the format:
# http(s)://sitename.com/?DeleteKey=
# where the case-sensitive key is defined below. Consider
# this a "get out of jail free" card in case something
# goes totally wrong.
set static::deletekey "DeleteAllBlacklistData"
# If set to true, log all blacklisted source IP addresses
# to the LTM log.
set static::log_offender 1
# If set to true, this will create unique tables per VS
# for blacklisting activities.
# This is good if you wish to apply the iRule across multiple
# virtuals but want blocking tables to be created for each
# individual virtual and not shared across multiple ones.
set static::unique_tables_per_virtual 1
# Display this block page for all attempts that hit the blacklist.
set static::blockpage {
Error
403 - Forbidden
Too many requests for invalid objects. Your access is denied.
}
}
when CLIENT_ACCEPTED {
# Collect the remote IP address.
set srcip [IP::remote_addr]
if { ($static::unique_tables_per_virtual ) } {
set blacklist_name "blacklist_[virtual]"
set countlist_name "[IP::remote_addr]_[virtual]"
} else {
set blacklist_name "blacklist"
set countlist_name "[IP::remote_addr]"
}
}
when HTTP_REQUEST {
# If the request has a DeleteKey parameter that matches
# the setting supplied in RULE_INIT for static::deletekey,
# delete the blacklist subtable (values and keys).
if { [URI::query [HTTP::uri] DeleteKey] equals "$static::deletekey" } {
log local0. "BLACKLIST: Table manually cleared by [IP::remote_addr]."
table delete -subtable $blacklist_name -all
}
}
when HTTP_RESPONSE {
# If the source IP is already in the blacklist table,
# respond with the block page.
if { [table lookup -subtable $blacklist_name $srcip] != "" } {
HTTP::respond 403 content "[subst $static::blockpage]"
return
# If the source IP has gotten a 404 but does not currently
# have a unique subtable entry, create a new offender.
} elseif { ([table lookup -subtable $countlist_name "count"] == "") && ([HTTP::status] equals "404") } {
set count 1
table add -subtable $countlist_name "count" $count indef $static::maxperiod
# If the source IP has a 404 and is a repeat offender,
# increment the count within the table.
} elseif { ([table lookup -notouch -subtable $countlist_name "count"] != "") && ([HTTP::status] equals "404") } {
set count [table incr -notouch -subtable $countlist_name "count"]
# If the maximum number of errors has been reached,
# add the source IP to the blacklist and get rid of the
# offender's unique table (he'll get a new one after his holdtime
# is over). Respond with the error page.
if { $count >= $static::maxerrors } {
table add -subtable $blacklist_name $srcip "blocked" indef $static::holdtime
table delete -subtable $countlist_name -all
HTTP::respond 403 content "[subst $static::blockpage]"
if { ($static::log_offender) } {
log local0. "BLACKLIST: [IP::remote_addr] is blacklisted on [virtual] for $static::holdtime seconds."
}
return
}
}
}