For more information regarding the security incident at F5, the actions we are taking to address it, and our ongoing efforts to protect our customers, click here.

Rate Limiting based on ACCESS TOKEN (OAuth 2.0)

Problem this snippet solves:

When publishing web services, you need to implement some rate limiting functions to avoid abuses. There are plenty of ways to setup Rate limiting

How to use this snippet:

The code below setup a rate limiting based on the ACCESS TOKEN. The client will receive a response "429 Too much requests" after 1000 requests in a window of 300 seconds.

The client can request its current status by doing a request to /rate_limit_status. He will then receive the following JSON message :

{ 
    "x-rate-limit-limit": 1000,
    "x-rate-limit-remaining": 800,
    "x-rate-limit-reset": 100
}

Code :

when RULE_INIT {
    ###
    # rate limit options
    ###
 
    set static::request_limit 1000
    set static::window_size 300
 
    ###
    # define URI endpoints
    ###
 
    set static::status_uri "/rate_limit_status"
}
 
when HTTP_REQUEST {
 
    ###
    # initialize vars
    ###
 
    set access_token ""
    set client_ip ""
 
    ###
    # retrieve the access_token. It will be used as a mandatory key to evaluate rate limiting
    ###
 
    if { [HTTP::header exists Authorization] and [HTTP::header Authorization] contains "Bearer" } {
        set access_token [getfield [HTTP::header Authorization] " " 2]
        set client_ip [IP::client_addr]
    }
 
    if { !($access_token eq "") } {
 
        ###
        # provide client with rate limit status
        ###
        set key [sha1 $access_token]
        set count [table lookup -notouch $key]
        set time [table timeout -remaining $key]
       
        ###
        # Provide a status page to the client
        ###
       
        if { [HTTP::path] eq $static::status_uri and [HTTP::method] eq "GET" } {
            if { $count > 0 } {
                set x_rate_limit_limit "$static::request_limit"
                set x_rate_limit_remaining "[expr {$static::request_limit-$count}]"
                set x_rate_limit_reset "$time"
            } else {
                set x_rate_limit_limit "$static::request_limit"
                set x_rate_limit_remaining "$static::request_limit"
                set x_rate_limit_reset "$static::window_size"
            }
            HTTP::respond 200 content "{\"x-rate-limit-limit\": $x_rate_limit_limit,\"x-rate-limit-remaining\": $x_rate_limit_remaining,\"x-rate-limit-reset\": $x_rate_limit_reset}" noserver Content-Type "application/json" Connection Close
            event disable all
        }  else {
       
            ###
            # Handle the case where a client reach the rate limit
            ###
           
            if { $count >= $static::request_limit } {
                set x_rate_limit_limit "$static::request_limit"
                set x_rate_limit_remaining "0"
                set x_rate_limit_reset "$time"
               
                HTTP::respond 429 content "{\"x-rate-limit-limit\": $x_rate_limit_limit,\"x-rate-limit-remaining\": $x_rate_limit_remaining,\"x-rate-limit-reset\": $x_rate_limit_reset}" noserver Content-Type "application/json" Connection Close
                event disable all
            } else {
                if { $count == 0 } {
                    table add $key 1 $static::window_size $static::window_size
                } else {
                    table incr -notouch $key
                }
            }
        }
    }
}

Tested this on version:

11.5
Updated Jun 06, 2023
Version 2.0

1 Comment

  • Subrun's avatar
    Subrun
    Icon for Cirrostratus rankCirrostratus

    at version 14.1.0.6 is this already part of f5 or still we need to make a rule ?