openresty / encrypted-session-nginx-module

encrypt and decrypt nginx variable values
http://openresty.org
195 stars 52 forks source link

set_encrypt_session destroys proxied variables #1

Closed clubpetey closed 11 years ago

clubpetey commented 11 years ago

I'm trying to encrypt a header that was returned from a proxy request., however, after executing set_encrypt_session, the $sent_http variable is blank. Here's a simple example

location = /api/login { proxy_pass http://127.0.0.1:8080/login;

more_set_headers -s 200 "Token: $sent_http_auth_token";
set_encrypt_session $session $sent_http_auth_token;
set_encode_base32 $session; 
more_set_headers -s 200 "Token2: $session";
more_set_headers -s 200 "Token3: $sent_http_token";

}

Assuming that the proxy request returns a header called "Auth-Token" the following code will result with the "Token" header containing the contents of the "Auth-Token" header, "Token2" will contain an encrypted empty string, and "Token3" Will not be printed, as $sent_http_token is null.

I've tried all sorts of combinations, but I cannot get set_encrypt_session to work with anything expect a static text string. Even a string and a variable (like "TEST${sent_http_auth_token}") results in a null string

agentzh commented 11 years ago

Hello!

On Tue, Jan 22, 2013 at 4:04 AM, Pete Mueller notifications@github.com wrote:

Assuming that the proxy request returns a header called "Auth-Token" the following code will result with the "Token" header containing the contents of the "Auth-Token" header, "Token2" will contain an encrypted empty string, and "Token3" Will not be printed, as $sent_http_token is null.

Well, this is a common pitfall for Nginx beginners.

Nginx directives are running in phases. So don't expect them always run in exactly the same order as you write them down in nginx.conf.

Basically, the set_encrypt_session and set_encode_base32 directives run in the "rewrite" phase, way before the "content" phase that proxy_pass runs at. So your config snippet is actually like this:

location = /api/login {
    set_encrypt_session $session $sent_http_auth_token;
    set_encode_base32 $session;

    proxy_pass http://127.0.0.1:8080/login;

    more_set_headers -s 200 "Token: $sent_http_auth_token";
    more_set_headers -s 200 "Token2: $session";
    more_set_headers -s 200 "Token3: $sent_http_token";
}

So there's no wonder when executing set_encrypt_session, $sent_http_auth_token is blank, because at that point Nginx has not yet even touched proxy_pass!

For more details on Nginx directive running order, you can read the English transcript for my Nginx online tutorials here:

http://openresty.org/#eBooks

To solve your problem, you can use the header_filter_by_lua directive provided by our ngx_lua module:

http://wiki.nginx.org/HttpLuaModule#header_filter_by_lua

And you can call set_encrypt_session and set_encode_base32 config directives directly from within Lua via the ndk.set_var.DIRECTIVE API:

http://wiki.nginx.org/HttpLuaModule#ndk.set_var.DIRECTIVE

You can also read and write response headers directly from within Lua, via the ngx.header.HEADER API:

http://wiki.nginx.org/HttpLuaModule#ngx.header.HEADER

Best regards, -agentzh

clubpetey commented 11 years ago

Hahahaha I was reading the openresty link as I got notice your email came in. I was just coming to the same conclusion. I see that lua would be an option, but is there a non-lua way to do this? Can you move commands to a different phase? Or can you create a sub-request that would run after the proxy_pass that could access the header AND still run set_encrypt_session? Maybe something with echo_subrequest_async?

The reason I ask is we don't use LUA for anything else, and I'd rather not load all the libraries and re-compile nginx with LUA support just for this one tricky point. I'd rather move set_encrypt_session to the content phase. this makes sense to me anyway, since the session data you want to encrypt many times would come from a proxy_pass-type call. But, putting set_decrypt_session makes sense in the rewrite phase, because you'd want the decrypted session data available to pass on to proxy calls.

Or I may not be using the encrypted session concept correctly.

Thanks very much for your well-documented description.

agentzh commented 11 years ago

Hello!

On Tue, Jan 22, 2013 at 1:21 PM, Pete Mueller notifications@github.com wrote:

Hahahaha I was reading the openresty link as I got notice your email came in. I was just coming to the same conclusion.

Good to know :)

I see that lua would be an option, but is there a non-lua way to do this? Can you move commands to a different phase? Or can you create a sub-request that would run after the proxy_pass that could access the header AND still run set_encrypt_session? Maybe something with echo_subrequest_async?

No, such work-arounds both won't work at all or much more expensive (in terms of runtime performance) than header_filter_by_lua.

The reason I ask is we don't use LUA for anything else, and I'd rather not load all the libraries and re-compile nginx with LUA support just for this one tricky point.

Well, firstly, it is Lua instead of LUA; secondly, it is highly recommended to enable Lua support in your Nginx, which is also very likely to help you at another time in the future (and also can simplify your Nginx.conf greatly).

If you have never tried the ngx_lua module, please try it, especially with the LuaJIT 2.0 engine instead of the standard Lua 5.1 interpreter :)

Thanks very much for your well-documented description.

You're welcome :)

Best regards, -agentzh

clubpetey commented 11 years ago

Thanks for all your help, I will work on installing Lua this evening. We'll give it a try.