Opened 4 years ago

Closed 4 years ago

#1934 closed defect (duplicate)

Unpredictable behaviour using proxy_cookie_path to add SameSite cookie attribute

Reported by: edrandall@… Owned by:
Priority: critical Milestone:
Component: documentation Version: 1.17.x
Keywords: Cc:
uname -a: Linux tszcwpp001 2.6.32-754.25.1.el6.x86_64 #1 SMP Wed Nov 20 15:07:26 EST 2019 x86_64 x86_64 x86_64 GNU/Linux
nginx -V: nginx version: nginx/1.17.6
built by gcc 4.4.7 20120313 (Red Hat 4.4.7-18) (GCC)
built with LibreSSL 3.0.2
TLS SNI support enabled
configure arguments: --prefix=/home/erandall/build/nginx-build/install/nginx-1.17.6 --with-pcre=/home/erandall/build/nginx-build/compile/pcre-8.43 --with-pcre-jit --with-zlib=/home/erandall/build/nginx-build/compile/zlib-1.2.11 --with-openssl=/home/erandall/build/nginx-build/compile/libressl-3.0.2 --with-http_ssl_module --with-http_v2_module --with-http_auth_request_module --with-http_slice_module --without-http_autoindex_module --without-http_browser_module --without-http_empty_gif_module --without-http_fastcgi_module --without-http_geo_module --without-http_grpc_module --without-http_memcached_module --without-http_mirror_module --without-http_scgi_module --without-http_split_clients_module --without-http_ssi_module --without-http_upstream_hash_module --without-http_upstream_ip_hash_module --without-http_upstream_least_conn_module --without-http_upstream_zone_module --without-http_userid_module --without-http_uwsgi_module --with-threads --with-file-aio

Description

Use-Case:

We are attempting to use alter cookie attibutes for the Chrome browser, in view of the upcoming SameSite changes per https://www.chromium.org/updates/same-site

Configuration

The nginx is configured as a proxy in front of apache-tomcat. SSL is terminated on Nginx. Session cookies are set by Tomcat. For architectural reasons we need to add the SameSite=None attribute at the nginx proxy layer for Chrome users.

Hence we have a configuration consisting of (heavily abbreviated):

http {

    map $http_user_agent    $samesite_attr {
        "~*chrome"    '; SameSite=None';
    }

    upstream local_tomcat {
	server        127.0.0.1:8080 fail_timeout=0;
        keepalive     100;
    }

    server {
        listen    9443 ssl http2;

        location / {
            proxy_pass           http://local_tomcat;
            proxy_cookie_path    ~/(.*)    "/$1$samesite_attr"; 
            
        }
    }
}

Expected behaviour

When the backend tomcat returns a set_cookie header and the user-agent string contains 'Chrome', we expected to see ; SameSite=None appended on the cookie attribute list.
(The cookie already contains the Secure attribute btw.)

Observed behaviour

Initially users on Chrome started to report website availability issues and 'gateway timeout' errors. Using a plugin to alter the Chrome user-agent string to impersonate IE10 reverted to normal working behaviour.

Further investigation using 'Curl' showed that, after Tomcat processing completes, sometimes the HTTP/2 response became corrupted. Downgrading to http1.1 allowed us to observe the Set-Cookie header - this sometimes contained a fragment of the User-Agent string interspersed within the value.

Examples:

1) Using HTTP/2 and user-agent: chrome:
Response:

* Connection state changed (MAX_CONCURRENT_STREAMS updated)!
* http2 error: Invalid HTTP header field was received: frame type: 1, stream: 1, name: [set-cookie], value: [JSESSION_blk-idp02=8991A33D964779B87765BE4CD56A2FF3; Path=/; SameSite=None]
* HTTP/2 stream 1 was not closed cleanly: PROTOCOL_ERROR (err 1)
* Closing connection 0
* TLSv1.2 (OUT), TLS alert, Client hello (1):
} [2 bytes data]
curl: (92) HTTP/2 stream 1 was not closed cleanly: PROTOCOL_ERROR (err 1)

2) Using HTTP/1.1 and a longer user-agent string incorporating the word chrome:
Tomcat set-cookie:
JSESSION_blk-idp02=6CD6436F1944BD877C73AE0EDAF301FB; Path=/userplatform; Secure; HttpOnly
Response:

< set-cookie: JSESSION_blk-idp02=6CD6436F1944BD877C73AE0EDAF301FB; Path=/ ; SameSite=Noneuserplatform; Secure; HttpOnly

We tried a number of different tactics to work-around the problem:

  • using two maps (to distance the user-agent string from the $samesite_attr value);
  • Removing the regex on proxy_cookie_path, instead hard-coding the cookie paths used by the application;

Unfortunately these were unsuccessful.

This leaves us concluding that there's an underlying issue with the proxy_cookie_path directive not being thread/memory safe.

Change History (3)

comment:1 by edrandall@…, 4 years ago

Component should be 'nginx-core' not 'documentation'

comment:2 by edrandall@…, 4 years ago

Tested also using nginx-1.17.8 built using the same options + pcre-8.44:

nginx version: nginx/1.17.8
built by gcc 4.4.7 20120313 (Red Hat 4.4.7-18) (GCC)
built with LibreSSL 3.0.2
TLS SNI support enabled
configure arguments: --prefix=/apps/nginx-build/install/nginx-1.17.8 --with-pcre=/apps/nginx-build/compile/pcre-8.44 --with-pcre-jit --with-zlib=/apps/nginx-build/compile/zlib-1.2.11 --with-openssl=/apps/nginx-build/compile/libressl-3.0.2 --with-http_ssl_module --with-http_v2_module --with-http_auth_request_module --with-http_slice_module --without-http_autoindex_module --without-http_browser_module --without-http_empty_gif_module --without-http_fastcgi_module --without-http_geo_module --without-http_grpc_module --without-http_memcached_module --without-http_mirror_module --without-http_scgi_module --without-http_split_clients_module --without-http_ssi_module --without-http_upstream_hash_module --without-http_upstream_ip_hash_module --without-http_upstream_least_conn_module --without-http_upstream_zone_module --without-http_userid_module --without-http_uwsgi_module --with-threads --with-file-aio

Using the following configuration:

map $http_user_agent $rfc6265bis_ua {
	"~*chrome"					'true';
}

map $rfc6265bis_ua $samesite_attr {
	'true'						'; SameSite=None';
}

proxy_cookie_path '/'				'/ $samesite_attr';
proxy_cookie_path '/userplatform'	'/userplatform $samesite_attr';

Getting this corruption on the set-cookie:

$ UA="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36"
$ curl  -S -s -H "user-agent: $UA" -v -o /dev/null https://devcwpp002.bfm.com:9502/userplatform/signOn
...
< set-cookie: JSESSION_blk-idp02=DAA2915DC8B992CEA820F84985462191; Path=/ ; SameSite=Noneuserplatform; Secure; HttpOnly

comment:3 by Maxim Dounin, 4 years ago

Resolution: duplicate
Status: newclosed

This looks like a variant of #564, but with proxy_cookie_path instead of rewrite. Further, it ends up with corrupted result since $1 refers to the capture from proxy_cookie_path when calculating resulting string length, and becomes empty when evaluating actual data.

A workaround would be to use named captures instead, for example:

proxy_cookie_path    ~/(?<foo>.*)    "/$foo$samesite_attr";

As for the configuration in comment:2, the result seems perfectly correct: since / matches cookie path /userplatform returned, / in it is replaced with the / $samesite_attr, resulting in Path=/ ; SameSite=Noneuserplatform, exactly as shown in your tests.

Closing this as a duplicate of #564.

Note: See TracTickets for help on using tickets.