Redirect Location header validator

Problem this snippet solves:

This iRule demonstrates how to validate the 30x redirects a web application sends back to clients. It intercepts any redirects which are to a domain that is not in a whitelist string data group. The goal is to protect clients from a web application which does not provide complete validation of user-input when generating HTTP redirects. See the OWASP Top Ten page for details on this class of vulnerability:

OWASP Top Ten Unvalidated Redirects and Forwards

Code :

# Validate the 30x redirects the web application sends to clients against a whitelist stored in a data group
# The goal is to prevent the client from being tricked into receiving an unvalidated redirect to a malicious third party site
# by an attacker.  See the OWASP Top Ten for details on the vulnerability: https://www.owasp.org/index.php/Top_10_2010-A10-Unvalidated_Redirects_and_Forwards 

# The data group format is string.  v11 example:
#
#ltm data-group internal my_domain_whitelist_dg {
#    records {
#        example.com { }
#        example.net { }
#        example.org { }
#        example.co.uk { }
#        partner.com { }
#    }
#    type string
#}

when RULE_INIT {
   # Name of the string data group which contains the whitelisted domains
   # (ex: legal.com, legal.net, okay.org, etc)
   set static::domain_whitelist_dg "my_domain_whitelist_dg"

   # Log debug to /var/log/ltm? 1=yes, 0=no.
   set static::redirect_debug 1
}
when HTTP_RESPONSE {

   # Check if domain is a redirect
   if {[HTTP::is_redirect]}{

      if {$static::redirect_debug}{
         log local0. "[IP::client_addr]:[TCP::client_port]: status=[HTTP::status], Location=[HTTP::header Location], Server=[LB::server]"
      }
      # Check if the redirect location is an absolute URL
      switch -glob [HTTP::header Location] {
         "/*" -
         "" {
            # No Location header value or a local reference so do not check against whitelist
            if {$static::redirect_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: Location local or empty. Not validating"}
         }
         default {
            # Check Location value against the data group
            set hostname [string tolower [URI::host [HTTP::header Location]]]
            if {$static::redirect_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: Checking $hostname against $static::domain_whitelist_dg"}

            # Check the hostname against the whitelist
            if {[class match $hostname ends_with $static::domain_whitelist_dg]}{
               if {$static::redirect_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: Legal redirect to [HTTP::header location]"}
            } else {
               if {$static::redirect_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: Illegal redirect to [HTTP::header location]"}
               HTTP::respond 302 Location "http://www.example.com/redirect_blocked.html"
            }
         }
      }
   }
}
Published Mar 18, 2015
Version 1.0
No CommentsBe the first to comment