Forum Discussion

James_Betts_290's avatar
Oct 19, 2016
Solved

ASM issue, need to return HTTP 500 to client in certain cases

I've written a script that captures the "Content-Type" header from requests. In the event that ASM blocks the request, I need to respond to the client with one of three types of responses (I'm using the ASM_REQUEST_BLOCKING event):

 

  1. If Content-Type = text/xml then send back a SOAP error with HTTP 200
  2. If Content-Type = application/json then return HTTP 500
  3. All others return the default ASM response with HTTP 200.

Cases 1 & 3 are handled, but I can't figure out how to force an HTTP 500 status to be returned to the client. HTTP_RESPONSE doesn't fire when ASM blocks.

 

Thanks for your advice.

 

  • This is not pretty but it works for the problem I had:

     

     Note:
     1. You must enable ASM events for the policy that you intend to invoke this with.
     2. Make a custom blocking response page that is nothing but spaces and CR/LF
        that is as large as the iFile that you will be putting in ASM::payload.
     3. In your iFile, use a "$" character where you want the ASM::support_ID to show
    
    when HTTP_REQUEST {
        set CT [string tolower [HTTP::header Content-Type]]
       log local0. "ASM-R-H: Content: $CT"
    }
    
    when ASM_REQUEST_BLOCKING {
       log local0. "ASM-R-H: Blocking Content: $CT"
    if the application type isn't SOAP then show the generic error
        if { ([string first $CT "application/soap"] < 0) && ([string first $CT "text/xml"] < 0) } {
            log local0. "ASM-R-H: HTML blocking [ASM::support_id]"
            ASM::payload replace 0 0 [string map "$ [ASM::support_id]" [ifile get "/Common/HTML-Error-Page"]]
            return
        }
    
    handle SOAP errors
       log local0. "ASM-R-H: XML blocking [ASM::support_id]"
        ASM::payload replace 0 0 [string map "$ [ASM::support_id]" [ifile get "/Common/SOAP-Error-Response"]]
        return
    }
    
    when ASM_REQUEST_DONE {
        if { not (([ASM::status] equals "blocked") || ([ASM::status] equals "alarmed")) } { return }
        if { ([string first $CT "application/json"] < 0) && ([string first $CT "application/javascript"] < 0) } { return }
        ASM::unblock
       log local0. "ASM-R-H: alarm status [ASM::status]"
        set JSONProblem [ASM::support_id]
    }
    
    when HTTP_RESPONSE {
        if { $JSONProblem == "" } { return }
       log local0. "ASM-R-H: alarm status $JSONProblem"
        HTTP::respond 500 content "Support ID $JSONProblem"
    }
    

     

6 Replies

  • First of all, both your cases 1 and 2 can be checked in HTTP_REQUEST, where you could use an HTTP::respond 200 or an HTTP::respond 500 however you wished. But let's presume that you meant something about Content-Type: application/json is triggering the ASM and you'd like to generate that 500 response as per the ASM being triggered.

    As you've found, you cannot initiate an HTTP::respond from within whichever ASM event you're using (e.g., ASM_REQUEST_BLOCKING). Instead, while you're in the ASM event set a variable and then hook the HTTP_RESPONSE_RELEASE event to modify the response that's going out to the client.

    For example:

     

    when HTTP_REQUEST {
        set asm_disliked_content_type 0
    }
    
    when ASM_REQUEST_BLOCKING {
        set asm_info [ASM::violation_data]
        if {[string first {VIOLATION_CONTENT_TYPE} [lindex $asm_info 0]] != -1} {
            set asm_disliked_content_type 1
        }
    }
    
    when HTTP_RESPONSE_RELEASE {
        if {$asm_disliked_content_type == 1} {
            HTTP::respond 500 content "Buzz Off"
            event disable all
            return
        }
    }
    

     

    Note that I made up VIOLATION_CONTENT_TYPE so this code won't work without adjustment... But the point is to show how you can use variables to maintain state from one event to another.

  • MVA's avatar
    MVA
    Icon for Nimbostratus rankNimbostratus

    You might be able to do this by allowing 500 in policy under "Allowed Response status codes" and then capturing the response code with "when HTTP_RESPONSE" and evaluate whether to allow or block at that point, in an iRule.

     

  • Mel, I believe "Allowed Response Status Codes" only applies to the responses generated by the protected application. Response codes generated on the F5 are permitted regardless of whether they're on the Allowed list or not. The purpose of "Allowed Response Status Codes" is to prevent a broken application from leaking too much information about itself when attacked.

     

    Also, as OP noted, HTTP_RESPONSE doesn't trigger if the ASM steps in and blocks the request before it's sent to the pool. HTTP_RESPONSE_RELEASE does, though.

     

  • This is not pretty but it works for the problem I had:

     

     Note:
     1. You must enable ASM events for the policy that you intend to invoke this with.
     2. Make a custom blocking response page that is nothing but spaces and CR/LF
        that is as large as the iFile that you will be putting in ASM::payload.
     3. In your iFile, use a "$" character where you want the ASM::support_ID to show
    
    when HTTP_REQUEST {
        set CT [string tolower [HTTP::header Content-Type]]
       log local0. "ASM-R-H: Content: $CT"
    }
    
    when ASM_REQUEST_BLOCKING {
       log local0. "ASM-R-H: Blocking Content: $CT"
    if the application type isn't SOAP then show the generic error
        if { ([string first $CT "application/soap"] < 0) && ([string first $CT "text/xml"] < 0) } {
            log local0. "ASM-R-H: HTML blocking [ASM::support_id]"
            ASM::payload replace 0 0 [string map "$ [ASM::support_id]" [ifile get "/Common/HTML-Error-Page"]]
            return
        }
    
    handle SOAP errors
       log local0. "ASM-R-H: XML blocking [ASM::support_id]"
        ASM::payload replace 0 0 [string map "$ [ASM::support_id]" [ifile get "/Common/SOAP-Error-Response"]]
        return
    }
    
    when ASM_REQUEST_DONE {
        if { not (([ASM::status] equals "blocked") || ([ASM::status] equals "alarmed")) } { return }
        if { ([string first $CT "application/json"] < 0) && ([string first $CT "application/javascript"] < 0) } { return }
        ASM::unblock
       log local0. "ASM-R-H: alarm status [ASM::status]"
        set JSONProblem [ASM::support_id]
    }
    
    when HTTP_RESPONSE {
        if { $JSONProblem == "" } { return }
       log local0. "ASM-R-H: alarm status $JSONProblem"
        HTTP::respond 500 content "Support ID $JSONProblem"
    }
    

     

    • James_Betts_290's avatar
      James_Betts_290
      Icon for Cirrus rankCirrus

      If there is a more elegant way to tackle this, I'd be interested in seeing it. Thanks to everyone who answered me.

       

  • Thank you very kindly for taking the time to assist me. Your advice helped lead me to the solution.