iRule for Brute Force Password Guessing Attacks
Problem this snippet solves:
Here I'm introducing an iRule for use as Brute Force Password Guessing Protection.
If you are running on 12.1.x you could solve this kind of attacks using ASM
https://support.f5.com/csp/article/K54335130
But this release it's also affected for one bug that could lead in some unwanted behavior.
The idea beneath this iRule is to workaround this bug and give a solution for those users that still run an ASM module on 12.1.x.
How to use this snippet:
This iRule requires a Data-Group to reference all the login pages in your infrastructure.
ltm data-group internal LOGIN_URI_DG { records { /app1/login { } /app2/login { } } type string }
In the STREAM expression you should identify how a login failure attempt is represented in your HTTP response. In my case, all login failures are represented by one JSON structure with the word "ACCESS_FAIL" in some of their fields.
STREAM::expression {=ACCESS_FAIL=}
You should also customize these global variables.
set static::maxtry 5 ; # Maximum failed login attempts set static::time 900 ; # Time window for failed login attempts evaluation [seconds] set static::bantime 3600 ; # How long the user is banned [seconds]
Code :
when RULE_INIT { # Variable Definition set static::maxtry 5 ; # Maximum failed login attempts set static::time 900 ; # Time window for failed login attempts evaluation [seconds] set static::bantime 3600 ; # How long the user is banned [seconds] } when CLIENT_ACCEPTED priority 100 { # Get client IP set source_ip [IP::remote_addr] # Reject user if exists in blacklist if { [table lookup -subtable "blacklist" $source_ip] != "" } { reject } } when HTTP_REQUEST priority 100 { event HTTP_RESPONSE enable event STREAM_MATCHED enable STREAM::disable set login_access 0 # Enable login access flag if { [class match [string tolower [HTTP::uri] ] contains LOGIN_URI_DG ] } { set login_access 1 } } when HTTP_RESPONSE priority 100 { if { $login_access } { # Configure stream expression when response is JSON if {[HTTP::header value Content-Type] starts_with "application/json"} { STREAM::expression {=ACCESS_FAIL=} STREAM::enable } event HTTP_RESPONSE disable } } when STREAM_MATCHED priority 100 { if { $login_access } { # Increase variable of login attempts set key "attempts:$source_ip" table add $key 0 indefinite $static::time set count [table incr $key] # If maximum login attempts value is exceeded, then include user in blacklist if { $count >= $static::maxtry } { table add -subtable "blacklist" $source_ip "blocked" indefinite $static::bantime table delete $key log local1. "User Rejected: $source_ip" } event STREAM_MATCHED disable } }
Tested this on version:
12.1Also good basic protection till AWAF license is in place 🙂