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.

HTTP POST redirect preserving POST data

Problem this snippet solves:

Use Javascript in an iRule to redirect HTTP POST requests to HTTPS. When an HTTP 30x redirect it sent to a client that has sent a POST request, the user-agent transparently issues a new GET request. As a result, the original POST request payload is lost.

The idea behind this iRule is when client sends a POST to an HTTP virtual server, LTM replies with an HTML page which contains a form. The form contains post-data that the client just sent. The form will be auto-submitted by Javascript via HTTPS.

Here is how this iRule works:

  1. if method = post, it issues HTTP::collect, which will invoke HTTP_REQUEST_DATA
  2. then it scans POST-data and prepares new content to respond to client. the new content will include
    • form with all INPUT field names retrieved from POST-data
    • the "action" parameter in the form points to external server
    • all input fields are set as hidden (so client won't see the data during the process)
    • Javascript that submits the form automatically
  3. then iRule replies to client with HTTP::respond command with content prepared in step 2

Code :

when RULE_INIT {
    set static::ext_url "https://10.10.71.3/test.post"
}
when HTTP_REQUEST {

   # Check if request was a POST
   if { [string tolower [HTTP::method]] eq "post" } {

      # Check if there is a Content-Length header
      if { [HTTP::header exists "Content-Length"] } {

         if { [HTTP::header "Content-Length"] > 1048000 }{

    # Content-Length over 1Mb so collect 1Mb
            set content_length 1048000

 } else {

    # Content-Length under 1Mb so collect actual length
            set content_length [HTTP::header "Content-Length"]
         }
      } else {

 # Response did not have Content-Length header, so use default of 1Mb
         set content_length 1048000
       }
       # Don't collect content if Content-Length header value was 0
       if { $content_length > 0 } {
          HTTP::collect $content_length
       }
    }    
}
when HTTP_REQUEST_DATA {
    set content "< script type=text/javascript language=javascript> \
        function s(){ document.f.submit(); }  \
        
" foreach p [split [HTTP::payload] &] { set name [URI::decode [getfield $p = 1]] set value [URI::decode [getfield $p = 2]] set content "${content}" } set content "${content}
" HTTP::respond 200 content $content }
Published Mar 18, 2015
Version 1.0

4 Comments

  • Hi,

     

    A 307 status code is created to ask the client to preserve the method and the content on the new location

     

  • @victor Plohod, try this irule

    when HTTP_REQUEST {    
         Check if the client used an SSL cipher 
        if {not ([catch {SSL::cipher version} result]) && [string tolower $result] ne "none"}{    
             Client did use a cipher 
            log local0. "\$result: $result. Allowing encrypted request." 
        } else { 
             Client did not use a cipher 
            log local0. "\$result: $result. Redirecting unencrypted request."
            if { [HTTP::method] eq "POST" } {
                HTTP::respond 307 "https://[HTTP::host]/"
            } else {
                HTTP::respond 302 "https://[HTTP::host]/"
            }
        } 
    }
    
  • Hello,

     

    Am curios about the statement:

     

    set static::ext_url ";

     

    is test.post generic and able to be used anywhere, or would I need to replace it with an entry specific to my environment?

     

    Thanks in advance,

     

    Chris