$upstream_http_* variables exist but do not seem to be readable

Hi,

I’m currently using OpenResty, and one of the things I am trying to do
is
have the backend send a specific header, and if that header is present,
run
a body_filter_by_lua call on the output. However, while I can use the
$upstream_http_* vars for populating (i.e. I can go add_header
SomeHeader
$upstream_http_foo, and if Foo has been sent see SomeHeader: FooVar in
the
output. However, all tests for them seem to break.

Example:

location @backend {
expires off;
proxy_pass http://_varnish;
set $test $upstream_http_csrf; # definitely exists, I
can
see it in the response headers
add_header SomeHeader $upstream_http_csrf; # And I can
read
it, but…
if ($upstream_http_csrf = 1) { # doesn’t matter if it’s
1,
“one” ~* “one”, anything…
# this block never gets called
}
add_header someOtherHeader $test; # Not present in
output as
empty
set_by_lua $use_token

if not ngx.var.sent_http_csrf == “” then
return ngx.var.upstream_http_csrf
end
return “baz”
'; # Always return ‘baz’
add_header WIllThisWork $use_token; # Again, empty
}

Posted at Nginx Forum:

Hello!

On Fri, Feb 1, 2013 at 6:48 AM, Maxim D. wrote:

the same - set_by_lua is executed before a response is available.

Yes, set_by_lua runs at exactly the same phase of the standard
ngx_rewrite module’s “set” directive. Actually set_by_lua is injected
into ngx_rewrite so that it can be mixed with other rewrite module
directives.

@shrikeh: you’re recommended to read my Nginx tutorials to learn more
about the Nginx config directive running order:

http://openresty.org/#eBooks

Best regards,
-agentzh

Hello!

On Fri, Feb 01, 2013 at 08:59:38AM -0500, shrikeh wrote:

location @backend {
expires off;
proxy_pass http://_varnish;
set $test $upstream_http_csrf; # definitely exists, I can
see it in the response headers

The “set” directive is rewrite module directive, and it is
executed during the rewrite request processing phase, which
happens before a request is passed to the upstream.

Unless the request was previusly processed using an upstream
server, the $test variable will be empty.

            add_header SomeHeader $upstream_http_csrf; # And I can read

it, but…

This will work.

            if ($upstream_http_csrf = 1) { # doesn't matter if it's 1,

“one” ~* “one”, anything…
# this block never gets called
}

This won’t, for the same reasons as outlined above - the “if”
directive can’t see upstream response headers as it’s executed
before a response is available.

            add_header someOtherHeader $test; # Not present in output as

empty

See above.

set_by_lua $use_token
'
if not ngx.var.sent_http_csrf  == "" then
  return ngx.var.upstream_http_csrf
end
return "baz"
'; # Always return 'baz'
            add_header WIllThisWork $use_token; # Again, empty

I’m not familiar with lua module, but likely the explanation is
the same - set_by_lua is executed before a response is available.


Maxim D.

Here is what I’m trying to achieve:

  1. Backend sends response header saying “this page requires filtering”.
  2. Lua script generates a token, and populates the appropriate tag in
    the
    body copy with the token.
  3. Token is pushed into user’s cookies and also stored in Redis.

From what I can gather, to do this, I need to:

  1. Add conditional logic within body_filter_by_lua based on the value of
    ngx.var.sent_http_csrf
  2. Generate the token
  3. Filter the body.
  4. Make a subrequest with the token to a separate internal location
    block so
    that Redis can save the token (as redis will be disabled within the
    context
    of body_filter_by_lua)

Posted at Nginx Forum:

I resolved this issue, the result is in this gist should anyone wish to
use
it: A simple nginx host file that, using the lua module, handles CSRF, rather than the backend having to (and thus generally breaking caching by having to use Set-Cookie). Here, the front end takes care of CSRF, and sends an X-CSRF-Valid header to the backend regarding the validity of the POST, so that it is advisory (the backend then choose whether or not to blow up about it). To cut down processing on every request, the CSRF parsing requires the backend to send an X-CSRF-Tokenize header, which also describes the token to pass for. Thus, a page that sends a header of X-CSRF-Tokenize : '::csrf::' will have all instances of '<input type="hidden" name="csrf" value="::csrf::" /> populated with a random hash. · GitHub

Thanks.

Posted at Nginx Forum:

Hi!

Thanks for the responses. Is there any way for nginx to work
conditionally
based on headers in the upstream response?

Posted at Nginx Forum: