15-Jul-2021
10:37
- last edited on
24-Mar-2022
01:06
by
li-migration
You can see my code about how to block users by source ip address that test if the servers and are vunrable to web attacks:
when RULE_INIT {
# The max requests served within the timing interval per the static::timeout variable
set static::maxReqs 3
# Timer Interval in seconds within which only static::maxReqs Requests are allowed.
# (i.e: 10 req per 2 sec == 5 req per sec)
# If this timer expires, it means that the limit was not reached for this interval and
# the request counting starts over. Making this timeout large increases memory usage.
# Making it too small negatively affects performance.
set static::timeout 60
}
#when CLIENT_ACCEPTED {
#set cIP_addr [IP::client_addr]
# set getcount [table lookup -notouch $cIP_addr]
# if { ! ( [class match $cIP_addr equals ip_whitelist] ) } {
# The following expects the IP addresses in multiple X-forwarded-for headers.
# It picks the first one. If XFF isn’t defined it can grab the true source IP.
# if { $getcount > $static::maxReqs } {
# log local0. "Request Count for $cIP_addr is $getcount"
# drop
# }
# }
#}
when HTTP_REQUEST {
# Allows throttling for only specific URIs. List the URIs_to_throttle in a data group.
# Note: a URI is everything after the hostname: e.g. /path1/login.aspx?name=user
if { [HTTP::header exists X-forwarded-for] } {
set cIP_addr [getfield [lindex [HTTP::header values X-Forwarded-For] 0] "," 1]
} else {
set cIP_addr [IP::client_addr]
}
set getcount [table lookup -notouch $cIP_addr]
if { ! ( [class match $cIP_addr equals ip_whitelist] ) } {
# The following expects the IP addresses in multiple X-forwarded-for headers.
# It picks the first one. If XFF isn’t defined it can grab the true source IP.
if { $getcount > $static::maxReqs } {
log local0. "Request Count for $cIP_addr is $getcount"
# drop
HTTP::respond 403 content {
<html>
<head><title>HTTP Request denied</title></head>
<body>Your HTTP requests are being blocked because of too many violations.</body>
</html>
}
}
}
}
#when ASM_REQUEST_BLOCKING {
when ASM_REQUEST_DONE {
#log local0.debug "\[ASM::status\] = [ASM::status]"
if { [ASM::status] equals "blocked" } {
# Allows throttling for only specific URIs. List the URIs_to_throttle in a data group.
# Note: a URI is everything after the hostname: e.g. /path1/login.aspx?name=user1
# if { [class match [HTTP::uri] equals URIs_to_throttle] } {
# The following expects the IP addresses in multiple X-forwarded-for headers.
# It picks the first one. If XFF isn’t defined it can grab the true source IP.
# set getcount [table lookup -notouch $cIP_addr]
if { $getcount equals "" } {
table set $cIP_addr "1" $static::timeout $static::timeout
# Record of this session does not exist, starting new record
# Request is allowed.
} else {
log local0. "Request Count for $cIP_addr is $getcount"
table incr -notouch $cIP_addr
# record of this session exists but request is allowed.
}
# }
}
}
EDIT:
Please carefully read documentation for the code as the ASM session tracking and session awerness (https://support.f5.com/csp/article/K02212345 ) are better futures as mentioned that can do simillar things but the idea for this irule to be less CPU intensive by blocking bad actors before the ASM processing.
20-Jul-2021 07:48
Here is an example with failover to using the client ip address if ASM fingerprint if it is equal to "0" (no device ID fingerprint available). As I mentioned better use the main irule for blocking by source IP address or for device fingerprint the session tracking and awareness (https://support.f5.com/csp/article/K02212345).
I just played with this as the ASM fingerprint is not aways generated for a web request and you need to first enable a feature like web scraping or in the newer versions the unified bot defense (see https://devcentral.f5.com/s/question/0D51T00006kGZft/asm-fingerprinting-with-irule and the general articles https://support.f5.com/csp/article/K19556739 ), so this irule can be helpfull if the default F5 features that use the fingerprint don't failover to client source IP address if there is no available fingerprint.
I also created 2 subtables for the ip addresses and fingerprints for optimizing the lookups.
Please read the full description of the code in:
https://devcentral.f5.com/s/articles/ASM-WAF-rate-limit-and-block-clients-by-source-ip-if-there-are-too-many-violations?page=1
when RULE_INIT {
# The max requests served within the timing interval per the static::timeout variable
set static::maxReqs 2
# Timer Interval in seconds within which only static::maxReqs Requests are allowed.
# (i.e: 10 req per 2 sec == 5 req per sec)
# If this timer expires, it means that the limit was not reached for this interval and
# the request counting starts over. Making this timeout large increases memory usage.
# Making it too small negatively affects performance.
set static::timeout 20
}
#when CLIENT_ACCEPTED {
#set cIP_addr [IP::client_addr]
# set getcount [table lookup -subtable "bad_ip" -notouch $cIP_addr]
# if { ! ( [class match $cIP_addr equals ip_whitelist] ) } {
# The following expects the IP addresses in multiple X-forwarded-for headers.
# It picks the first one. If XFF isn’t defined it can grab the true source IP.
# if { $getcount > $static::maxReqs } {
# log local0. "Request Count for $cIP_addr is $getcount"
# drop
# }
# }
#}
when HTTP_REQUEST {
# Allows throttling for only specific URIs. List the URIs_to_throttle in a data group.
# Note: a URI is everything after the hostname: e.g. /path1/login.aspx?name=user
if { [HTTP::header exists X-forwarded-for] } {
set cIP_addr [getfield [lindex [HTTP::header values X-Forwarded-For] 0] "," 1]
} else {
set cIP_addr [IP::client_addr]
}
set getcount1 [table lookup -subtable "bad_ip" -notouch $cIP_addr]
if { ! ( [class match $cIP_addr equals ip_whitelist] ) } {
# The following expects the IP addresses in multiple X-forwarded-for headers.
# It picks the first one. If XFF isn’t defined it can grab the true source IP.
if { $getcount1 >= $static::maxReqs } {
log local0. "Request Count for $cIP_addr is $getcount1"
# drop
HTTP::respond 403 content {
<html>
<head><title>HTTP Request denied</title></head>
<body>Your HTTP requests are being blocked because of too many violations.</body>
</html>
}
}
}
}
when ASM_REQUEST_DONE {
log local0.debug "\[ASM::status\] = [ASM::status] and ASM_Device_id = [ASM::fingerprint] "
if { [ASM::status] equals "blocked" } {
# Allows throttling for only specific URIs. List the URIs_to_throttle in a data group.
# Note: a URI is everything after the hostname: e.g. /path1/login.aspx?name=user1
if { not ( [class match [IP::client_addr] equals whitelist] ) } {
# The following expects the IP addresses in multiple X-forwarded-for headers.
# It picks the first one. If XFF isn’t defined it can grab the true source IP.
set cIP_finger [ASM::fingerprint]
if { $cIP_finger == 0 } {
if { $getcount1 equals "" } {
table set -subtable "bad_ip" $cIP_addr "1" $static::timeout $static::timeout
# Record of this session does not exist, starting new record
# Request is allowed.
} else {
log local0. "Request Count for $cIP_addr is $getcount1"
table incr -subtable "bad_ip" -notouch $cIP_addr
# record of this session exists but request is allowed.
}
} else {
set getcount2 [table lookup -subtable "bad_id" -notouch $cIP_finger]
if { $getcount2 equals "" } {
table set -subtable "bad_id" $cIP_finger "1" $static::timeout $static::timeout
# Record of this session does not exist, starting new record
# Request is allowed.
} else {
if { $getcount2 >= $static::maxReqs } {
log local0. "Request Count for $cIP_finger is $getcount"
table incr -subtable "bad_id" -notouch $cIP_finger
# record of this session exists but request is allowed.
} else {
#drop
HTTP::respond 403 content {
<html>
<head><title>HTTP Request denied</title></head>
<body>Your HTTP requests are being throttled.</body>
</html>
}
}
}
}
}
}
}