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.11 Comment
Also good basic protection till AWAF license is in place 🙂