Forum Discussion

Mats_Nyström's avatar
Mats_Nyström
Icon for Nimbostratus rankNimbostratus
Oct 13, 2016

Help needed: HTTP::respond does not add headers

I am struggling with HTTP::respond and adding headers.

This is an outline of what I am trying to do:

 

when HTTP_RESPONSE {

         Save response-headers for later use
        set headers ""
        foreach aHeader [HTTP::header names] {
            set headers [concat $headers $aHeader [HTTP::header value $aHeader]]
        }
        log local0. "Headers Saved: $headers"

         Fetch "x-http-statuscode" for later use             
        if {[HTTP::header exists "x-http-statuscode"]} {
            set xhttpstatuscode [lindex [HTTP::header values x-http-statuscode] 0]
        }

        if {[HTTP::status] == 200 && [expr {$xhttpstatuscode != 200}]} {
            HTTP::respond $xhttpstatuscode -version [HTTP::version] content [HTTP::payload] $headers
        }
}

 

I can't get HTTP::repond to add the "original headers"!

Help is appreciated!

1 Reply

  • Hi Mats,

    you have to use the [eval] command to allow the TCL interpreter to expand the $headers variable before executing the [HTTP::respond] command. Otherwise the collected headers storend in $headers will be counted just as a single parameter.

    Warning: Special care must be taken if you use [eval]. The problem of this technique is, that the input may contain TCL syntax (e.g. [somecommand] and $variables) which may accedentially become executed by the [eval] command (aka. its comparable with SQL-Injection for TCL). In the best case this will lead to a broken TCP connection, but in the worst case could also crash your TMM core or even lead into a full compromise of your TMM/TCL environment.

    You may take a look to the snipped below to get familar with the usage of [eval] and to learn a syntax how the header values can be securely collected and then passed to eval.

     

    when HTTP_RESPONSE {
        if { ( [HTTP::header exists "x-http-statuscode"] ) 
         and ( [HTTP::status] == 200 ) 
         and ( [set xhttpstatuscode [lindex [HTTP::header values "x-http-statuscode"] 0]] != 200 ) } then {
    
             Save response-headers for later use
            set headers ""
            set x 0
            foreach aHeader [HTTP::header names] {
                incr x
                set input_array(name_$x) $aHeader
                set input_array(value_$x) [HTTP::header value $aHeader]
                append headers "\$input_array(name_$x) \$input_array(value_$x) "
    
                 Explanation: The names and values of your headers are getting temporary stored in a $input_array() variable.
                              This technique makes sure, that the later use of [eval] never interprets the contained values 
                              as TCL syntax. This technique is comparable to a parameterised SQL query to counter SQL injections. 
    
            }
    
            log local0. "Headers Saved: $headers"
    
            eval "HTTP::respond $xhttpstatuscode -version [HTTP::version] content {[HTTP::payload]} $headers"
    
             Explanation: [eval] uses two passes in its execution, the first pass will concentate [command]s 
                          and $variables into eval's input string. And the second pass will execute the input string 
                          as TCL code and concentate [command]s and $variables a second time.
            
             Raw TCL syntax:
            
             eval "HTTP::respond $xhttpstatuscode -version [HTTP::version] content {[HTTP::payload]} $headers"
            
             First pass substitution result:
            
             eval "HTTP::respond 404 -version 1.1 content [HTTP::payload] $input_array(name_1) $input_array(value_1) $input_array(name_2) $input_array(value_2)"
            
             Second pass code execution:
            
             HTTP::respond 404 -version 1.1 content {...} {MyHeader1} {MyHeaderValue1} {EvilHeader} {Evil\[InsertTCLCodeHere\]HeaderValue}
    
        }
    }
    

     

    Additional Note: The [HTTP::payload] command will not output the response payload unless [HTTP::collect] is called and the HTTP_RESPONSE_DATA event is raised.

    *Update: Modified my previous post to include the parameterised eval syntax.

    Cheers, Kai