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

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] != "" } {

when HTTP_REQUEST priority 100 {
    event HTTP_RESPONSE enable
	event STREAM_MATCHED enable
	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=}
		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:

Published Feb 07, 2020
Version 1.0

Was this article helpful?