Forum Discussion

abhinay's avatar
abhinay
Icon for Nimbostratus rankNimbostratus
Dec 27, 2022

iRule for HTTP Post Method to match form data in the body

Please let me know how to match formdata "key" and "value" using HTTP Method "POST" from its Body. 

From below I would like to match key "func" its value that contains "qds."

 

"body": {
					"mode": "formdata",
					"formdata": [
						{
							"key": "func",
							"value": "qds.ObjAction",
							"type": "default"
						},

 

9 Replies

  • I am trying the below after collecting the payload but it is not working. For form-data do we have to use something else to match? FYI the below works for form-urlencoded?

     

    when HTTP_REQUEST_DATA {

    if { [HTTP::method] equals "POST" }{
    set http_request_body "?[HTTP::payload]"
    if { [URI::query $http_request_body func] contains "qds." } {
    HTTP::respond 403 content "Forbidden" "Content-Type" "text/html"
    } }
    }

    • abhinay's avatar
      abhinay
      Icon for Nimbostratus rankNimbostratus

      mihaicand CA_Valli, Sorry for tagging you here. Both your solutions worked for me in my previous post and was looking for your help in combining two POST methods.

      The second requirement is {Method is POST - (URI contains "/cs" OR "llisapi.dll") AND (body contains Key "func" which contains Value "qds.") AND (body contains "_REQUEST" which contains Value "SYNDICATION_REQUEST" )} for which I am using below iRule and combined it with (body contains Key "_fInArgs=" which contains Value "=#").

      From this iRule I am observing that whatever is the second "if statement" it is returning Forbidden, but the first "if statement" is returning  "Could not get response". Tried interchanging the if statements but same issue, the second works and first doesnt.

      Any help will be appreciated.

       

      when HTTP_REQUEST {
      if {[HTTP::method] eq "POST"}{
      # Trigger collection for up to 1MB of data
      if {[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 { [HTTP::method] equals "POST" }{
      # Extract the entire HTTP request body and escape it to become a HTTP::uri string (for easier parsings)
      set http_request_body "?[HTTP::payload]"
      log local0. "http payload: $http_request_body"
      # Try to parse type value from the HTTP request body.
      if { [URI::query $http_request_body _fInArgs%3D] contains "%3D%23" } {
      HTTP::respond 403 content "You don't have authorization to view this page. Access Denied" noserver Content-Type text/html Connection Close Cache-Control no-cache
      } }
      if { ([HTTP::method] equals "POST") and ([HTTP::uri] contains "/cs" or [HTTP::uri] contains "llisapi.dll" )}{
      # Extract the entire HTTP request body and escape it to become a HTTP::uri string (for easier parsings)
      set varB [findstr [HTTP::payload] "func"]
      set varC [findstr [HTTP::payload] "_REQUEST"]
      if { ($varB contains "qds.") and ($varC contains "SYNDICATION_REQUEST")} {
      HTTP::respond 403 content "You don't have authorization to view this page. Access Denied" noserver Content-Type text/html Connection Close Cache-Control no-cache
      }
      }
      }


       

      • CA_Valli's avatar
        CA_Valli
        Icon for MVP rankMVP

        Hello abhinay , I've scripted the irule, now testing it in my lab.
        Will update you shortly 

    • abhinay's avatar
      abhinay
      Icon for Nimbostratus rankNimbostratus

      Hi JRahm, I was able to achieve this using findstr with below iRule.

       

      when HTTP_REQUEST {
      if {[HTTP::method] eq "POST"}{
      # Trigger collection for up to 1MB of data
      if {[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 { ([HTTP::method] equals "POST") and ([HTTP::uri] contains "/cs" or [HTTP::uri] contains "llisapi.dll" )}{
      # Extract the entire HTTP request body and escape it to become a HTTP::uri string (for easier parsings)
      set varB [findstr [HTTP::payload] "func"]
      set varC [findstr [HTTP::payload] "_REQUEST"]
      if { ($varB contains "qds.") and ($varC contains "SYNDICATION_REQUEST")} {
      HTTP::respond 403 content "You don't have authorization to view this page. Access Denied" noserver Content-Type text/html Connection Close Cache-Control no-cache
      }
      }
      }

  • xuwen's avatar
    xuwen
    Icon for Cumulonimbus rankCumulonimbus

    It would be pretty impractical to use iRules TCL to perform the translation POST JSON data. However, this task is trivial for iRules LX because of the power of Node.js

    rules>>json_post

    when HTTP_REQUEST {
        # Collect POST data
        if { [HTTP::method] eq "POST" }{
            set cl [HTTP::header "Content-Length"]
            HTTP::collect $cl
        }
    }
    when HTTP_REQUEST_DATA {
        # Send data to Node.js
        set handle [ILX::init "ilxlab2_plugin" "ilxlab2_ext"]
        if {[catch {ILX::call $handle jsonPost [HTTP::payload]} result]} {
          # Error handling
          log local0.error  "Client - [IP::client_addr], ILX failure: $result"
          HTTP::respond 400 content "<html>There has been an error.</html>"
          return
        }
        log local0. "result is $result"
        # Replace Content-Type header and POST payload
        # HTTP::header replace "Content-Type" "application/json"
        # HTTP::payload replace 0 $cl $result
        if { [lindex $result 0] > 0 } {
            HTTP::respond 400 content "<html>The following error occured: Invalid JSON</html>"
        } else {
            if { [lindex $result 1] contains "qds" } {
                HTTP::respond 403 content "Forbidden Access!"
            } else {
                HTTP::respond 400 content "value is [lindex $result 1]"
            }
        }
    }
    
    
    

    ilxlab2_ext>>index.js

    'use strict' // Just for best practices
    // Import modules here
    var f5 = require('f5-nodejs');
    // var qs = require('querystring'); // Used for parsing the POST data querystring
    
    // Create an ILX server instance
    var ilx = new f5.ILXServer();
    
    // This method will transform POST data into JSON
    ilx.addMethod('jsonPost', function (req, res) {
      // Get POST data from TCL and parse to JS object
      //var postData = qs.parse(req.params()[0]);
      var postData = req.params()[0];
      try {
        var jsonData = JSON.parse(postData);
      } catch (err) {
          console.log('Error with JSON.parse: ' + err.message);
          return res.reply(1);
      }
      if ( jsonData.formdata[0].key == "func" ) {
          console.log(jsonData.formdata[0].value);
          var qds_value = jsonData.formdata[0].value;
          return res.reply([0, qds_value])
      } else {
          return res.reply([0, "no"])
      }
      
    
      // Turn postData object into JSON and return to TCL
      // res.reply(JSON.stringify(postData));
    });
    
    //Start the ILX server
    ilx.listen();
    
    
    
    
    

     curl test command is:

    curl -kv http://10.10.10.177 -X POST -H "Content-Type: application/json" -d '{"mode":"formdata","formdata":[{"key":"func","value":"qds.ObjAction","type":"default"}]}'

    result is:

    [root@f5:LICENSE EXPIRES IN 4 DAYS:Active:Standalone] config # curl -kv http://10.10.10.177 -X POST -H "Content-Type: application/json" -d '{"mode":"formdata","formdata":[{"key":"func","value":"qds.ObjAction","type":"default"}]}'
    Note: Unnecessary use of -X or --request, POST is already inferred.
    * Rebuilt URL to: http://10.10.10.177/
    *   Trying 10.10.10.177...
    * Connected to 10.10.10.177 (10.10.10.177) port 80 (#0)
    > POST / HTTP/1.1
    > Host: 10.10.10.177
    > User-Agent: curl/7.47.1
    > Accept: */*
    > Content-Type: application/json
    > Content-Length: 88
    > 
    * upload completely sent off: 88 out of 88 bytes
    * HTTP 1.0, assume close after body
    < HTTP/1.0 403 Forbidden
    < Server: BigIP
    * HTTP/1.0 connection set to keep alive!
    < Connection: Keep-Alive
    < Content-Length: 17
    < 
    * Connection #0 to host 10.10.10.177 left intact
    Forbidden Access![root@f5:LICENSE EXPIRES IN 4 DAYS:Active:Standalone] config # 

    tail -f /var/log/ltm

    Dec 30 15:20:48 f5 info tmm[10644]: Rule /Common/ilxlab2_plugin/json_post <HTTP_REQUEST_DATA>: result is 0 qds.ObjAction
    Dec 30 15:20:48 f5.bigip-gtm110.com info sdmd[5171]: 018e0017:6: pid[20389]  plugin[/Common/ilxlab2_plugin.ilxlab2_ext] qds.ObjAction