Forum Discussion

PG0581's avatar
PG0581
Icon for Cirrus rankCirrus
Dec 16, 2022

iRule to filter on URIs and strings in the body of the payload

Hello, I am working on another iRule to filter on a couple of URIs and a couple of strings in the body of the payload, when a POST is made from the client, and if these all match drop.

So far from what I have found in my search, I think I can use "HTTP::collect" and then "findstr [HTTP::payload]" to find the strings in the payload (I also am of the understanding that string may not appear within the first 1 MB of the payload), I am just not sure what I posted below is going to work or not; it likely needs to be tweaked. 

Any feedback would be appreciated! 

 

 

create ltm data-group internal uri-list records add { abc { } def { } } type string

when HTTP_REQUEST {
  if {[HTTP::method] eq "POST"}{
    #Evaluate URI and trigger the collection for up to 1MB of data
    if { [class match [string tolower [HTTP::uri]] contains uri-list] and [HTTP::header "Content-Length"] ne "" && [HTTP::header "Content-Length"] <= 1048576}{
      set content_length [HTTP::header "Content-Length"]
    } else {
        set content_length 1048576
    }
    #Check if $content-length is not set to 0
    if { $content_length > 0} {
      HTTP::collect $content_length
    }
  }
}
when HTTP_REQUEST_DATA {
  if { [findstr [HTTP::payload] "abc" and "xyz" 3 &] }{
  log local0. "Denied: [IP::client_addr] - [HTTP::uri] - [findstr [HTTP::payload]]"
  HTTP::respond 403 content "Forbidden" "Content-Type" "text/html"  
}

 

 

 

3 Replies

  • HI PG05851,

    the iRule you have posted includes several issues that will disrupt your application.

    1.) You put too much checks in one if statement. If for an example the POST request is not in the uri-list, you will always start to HTTP::collect 1MB data. If the post request from the client is less than 1MB, than this would stall the connection.

    2.) You did not checked if Content-Length contains valid data. This may flood you logfiles with TCL errors if someone does silly things.

    3.) The syntax of the [findstr] command in wrong. Findstr is a command to perform a string cutting operation for a single input. You can not use an "and" operator in between. 

    You may check the modified iRule below...

     

    when HTTP_REQUEST {
    
        # Check for POST request send to known URI and contains payload to analyse
    	
    	if { ( [HTTP::method] eq "POST" ) 
    	 and ( [class match -- [string tolower [HTTP::path]] contains uri-list] )
    	 and ( [HTTP::header value "Content-Length"] >= 1 ) } then {
    		
    		# Perform input validation and check for configured upper limits
    		 
    		if { not [string is digit [HTTP::header value "Content-Length"] } then {
    			
    			# Malformed Content-Length header...
    			
    			HTTP::respond 400 content "BAD REQUEST"			
    			
    		} elseif { [HTTP::header value "Content-Length"] <= 1048576 } then {
    			
    			# Less than upper limit...
    			
    			HTTP::collect [HTTP::header value "Content-Length"]
    			
    		} else {
    
    			# Bigger than upper limits. Collecting only till upper limit...
    			
    			HTTP::collect 1048576
    			
    		}
    	} else {
    		# No need to collect...
    	}
    }
    when HTTP_REQUEST_DATA {
    	
    	# Check for "forbidden strings" (use lowercase rules) in the payload...
    	
    	switch -glob -- [string tolower [HTTP::payload]] {
    		"*f o rbi dd en s tring*" {
    			# Found a forbidden string...
    			log local0.debug "Denied: [IP::client_addr] - [HTTP::path] - *f o rbi dd en s tring*"
    			HTTP::respond 403 content "Forbidden" "Content-Type" "text/html"  
    		}
    		"*for bid den string*"  {
    			# Found a forbidden string...
    			log local0.debug "Denied: [IP::client_addr] - [HTTP::path] - *for bid den string*"
    			HTTP::respond 403 content "Forbidden" "Content-Type" "text/html"  
    		}
    		"*fo rbi dden str ing*" {
    			# Found a forbidden string...
    			log local0.debug "Denied: [IP::client_addr] - [HTTP::uri] - *fo rbi dden str ing*"
    			HTTP::respond 403 content "Forbidden" "Content-Type" "text/html"  
    		}
    	}
    }

     

    You could also use a Data-Group based approach in the HTTP_REQUEST_DATA even if your like...

     

    when HTTP_REQUEST_DATA {
    	
    	# Check for "forbidden strings" (use lowercase rules) in the payload...
    	
    	if { [set match_string [class match -name -- [string tolower [HTTP::payload]] contains black-list ] ne "" } then {
    			# Found a forbidden string...
    			log local0.debug "Denied: [IP::client_addr] - [HTTP::uri] - $match_string"
    			HTTP::respond 403 content "Forbidden" "Content-Type" "text/html"  
    	}
    }
    

     

    Depending on how the payload actually looks like and what you are trying to block, your may need other adjustments in HTTP_REQUEST_DATA event.

    Cheers, Kai

    • PG0581's avatar
      PG0581
      Icon for Cirrus rankCirrus

      Thanks for this Kai_Wilke. I will likely be picking this up again in the new year and can let you know how I make out.

  • Hello PG0581,

    I see it is going to fly but I recommend to delete this part >>>>  [HTTP::header "Content-length"] ne "" &&  and the rest of the I-rule seems to be fine.