Forum Discussion

Hannes_Rapp's avatar
Hannes_Rapp
Icon for Nimbostratus rankNimbostratus
Feb 11, 2016

Basic Auth iRule - Including Max Guessing Attempts and A Lockout Timer

Hello,

I have created a Basic Auth iRule which has a 5-attempts-max mechanism appended to it. I'm missing a Lockout Timer feature.

Goal/purpose: For this client, I need a temporary authentication mechanism which will be used for X number of months. The Basic Auth solution is a pre-live access restriction on a publicly available environment. Client would prefer not to go for the APM module.

Question(s): How to implement a Lockout Timer (to HTTP Username or Client IP) of 5 minutes when 5 failed guesses have been made? My current ideas are either to go for a sub-table solution, or a global-variable solution. Which one would be better for this given scenario (from the perspective of performance and efficient appliance memory usage)? Any other ideas are welcome

The current iRule can be found below:

when CLIENT_ACCEPTED {

  set authAttempt 1
  set byPass 0
  if { [class match [IP::client_addr] equals "private_net"] } {
    set byPass 1
  }
}
when HTTP_REQUEST {

  if { $byPass == 0 }{
    binary scan [ md5 [HTTP::password]] H* password
    if { $authAttempt > 5 }{
      log user.notice "User <[HTTP::username]>. Access Denied"
      HTTP::respond 403 content "Access RestrictedAuthentication failed. You have tried too many times, try again later." Connection Close
    } elseif { [class lookup [HTTP::username] data_approved_remote_users] ne $password } {
       log user.notice "User <[HTTP::username]>. Authentication attempts <$authAttempt/5>"
      HTTP::respond 401 WWW-Authenticate "Basic realm=\"Restricted area. Attempt $authAttempt/5. Note: IE browser will only allow 3 attempts\""
      incr authAttempt
      return
    } else {
      log user.notice "User <[HTTP::username]>. Access Granted"
      set byPass 1
    }
  }
}

2 Replies

  • Hi Hannes,

     

    I've added some code snippets to your iRule, which I've used in the past to create sliding window based account lockouts...

     

    when RULE_INIT {
        set static::failed_auth_limit_count 5           ; Max trys
        set static::failed_auth_limit_duration 300      ; Sliding Window in seconds
        set static::failed_auth_lockout_duration 600    ; Fixed duration in seconds
    }
    when CLIENT_ACCEPTED {
        if { [class match [IP::client_addr] equals "private_net"] } {
            set byPass 1
        } else {
            set byPass 0
        }
    }
    when HTTP_REQUEST {
        if { $byPass == 0 }{
            if { [catch {
                set username [HTTP::username]
                set password [HTTP::password]
            }]} then {
                HTTP::respond 500 content "Invalid Credentials"
                return
            }
            if { ( $username ne "" ) and ( $password ne "" ) } then {
                if { [table lookup -notouch "lck_$username"] ne "" } then {
                    HTTP::respond 403 content "Access RestrictedAuthentication failed. You have tried too many times, try again later." Connection Close
                    return
                } else {
                    binary scan [md5 $password] H* password
                    if { [set class_result [class lookup "$username" data_approved_remote_users]] eq $password } then {
                        log user.notice "User <$username>: Access Granted"
                        return
    
                        Warning: You shouldn't use "set byPass 1" for authenticated users. 
                                Certain forward proxys MAY share TCP sessions across different users. 
                                So you should Basic authenticate every single request.
    
                    } elseif { $class_result ne "" } then {
                        log user.notice "User <$username>: Access Denied"
                        if { [set authAttempt [expr { [table keys -subtable -count "lcks_$username"] + 1 }]] >= $static::failed_auth_limit_count } then {
                            log user.notice "User <$username>: Locked out!"
                            table set -notouch "lck_$username" X indef $static::failed_auth_lockout_duration
                            table delete -subtable "lcks_$username" -all
                        } else {
                            log user.notice "User <$username>: $authAttempt failed attempts!"
                            table set -subtable "lcks_$username" [clock clicks] "" indef $static::failed_auth_limit_duration
                        }
                        HTTP::respond 401 WWW-Authenticate "Basic realm=\"Restricted area. Attempt $authAttempt/5. Note: IE browser will only allow 3 attempts\""
                        return
                    }
                }
            }
            HTTP::respond 401 WWW-Authenticate "Basic realm=\"Restricted area\""
        }
    }
    

    Note: Didn't saved the iRule. Feel free to find the missing brackets... 😉

     

    Cheers, Kai