fingerprint
1 TopicASM/WAF rate limit and block clients by source ip (or device_id fingerprint) if there are too many violations
Problem this snippet solves: For attackers that cause too many violations it is better to block them at the start of the HTTP_REQUEST or CLIENT_ACCEPTED events as the ASM/WAF processing causes too much CPU and the CLIENT_ACCEPTED and HTTP_REQUEST events are before the ASM and the ASM irule events and this may help the CPU a little. In additon many services offer generation of device id/fingerprint like apple for ios or google for android or TMIXID for web or phone with SDK(https://money.tmx.com/en/quote/ID) , that is saved in HTTP header (https://clouddocs.f5.com/api/irules/HTTP__header.html) and you can make irule to use the HTTP header as a fingerprint value and just modify the irule code below! How to use this snippet: You can use this code with the variables set static::maxReqs 3 and set static::timeout 60 also in the code also the CLIENT_ACCEPTED event can be uncommented and the HTTP_REQUEST event coommented but if the clients come by using a NAT device before the F5 better use the X-Forwarded-For header to get the client ip address and rate limit by it. Also the data group ip_whitelist needs to be created maybe for not blocking vunrability tests and etc. Also the event ASM_REQUEST_DONE and [ASM::status] equals "blocked" can be replaced with ASM_REQUEST_BLOCKING but when ASM_REQUEST_BLOCKING happens after them, so this is why I use ASM_REQUEST_DONE and [ASM::status] equals "blocked" I got the idea from: https://clouddocs.f5.com/training/community/irules/html/class2/module1/lab2.html https://support.f5.com/csp/article/K07150625 Don't forget to enable the asm irule events under the security policy that Virtual server where the irule is attached uses (I forgot and it took me 30 minutes of testing to figure it out 😞 😞 https://techdocs.f5.com/kb/en-us/products/big-ip_asm/manuals/product/asm-implementations-11-6-0/29.html Also the irule can be modified to use a subtable "table set -subtable asm_violations" for example for the RAM memory limits and also the table ans subtable are shared between VIP servers, so another VIP even without ASM policy can check the same table or subtable and block the user. If you add the between line 15 and 32 to another VS even without ASM the VS will check the RAM table and block users by source ip that tried to attack the VS with the ASM policy. https://clouddocs.f5.com/api/irules/table.html https://devcentral.f5.com/s/articles/v101-the-table-command-subtables This way you can use the primary VS as a honeypot that allows all the other servers to learn the bad client's activities. For how to redirect to a honeypot VS with the ASM policy you can check: https://devcentral.f5.com/s/question/0D51T00008aWVfBSAW/what-is-f5-asm-conviction-and-can-it-be-used-for-configuring-custom-url-honey-pot-trap For blocking with the device_id fingerprint see the comment section. The fingerprint device id lookup may return "0" and then the lookup will failover to using source ip addresses. Also the new example uses 2 different subtables for device id or client ip addresses. In additon many services offer generation of device id/fingerprint like apple for ios or google for android or TMIXID for web or phone with SDK(https://money.tmx.com/en/quote/ID) , that is saved in HTTP header and you can make irule to use the HTTP header (https://clouddocs.f5.com/api/irules/HTTP__header.html) as a fingerprint value and just modify the irule code below! Code : 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] ) && ( [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. if { $getcount > $static::maxReqs } { log local0. "Request Count for $cIP_addr is $getcount" # drop HTTP::respond 403 content { Your HTTP requests are being blocked because of too many violations. } } } } #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. } # } } } Tested this on version: 15.14.3KViews0likes4Comments