Contao: CSRF-Token kommt durcheinander und ein Login ist nicht mehr möglich

Created on 7 Sep 2020  Â·  34Comments  Â·  Source: contao/contao

Affected version(s)

Contao 4.9

Description

In einem Projekt ist uns jetzt aufgefallen, dass der CSRF-Mechanismus durcheinander gebracht werden kann, so dass kein Login für mit Mitglieder mehr möglich ist. Aufgefallen war uns das beim IE11, das lässt sich aber auch bei anderen reproduzieren, wenn auch nicht so einfach. Ausgangspunkt:

  • Eine Site mit Mitgliedern.
  • eine 404-Umleitung auf ein Seite (z.b. Startseite oder Loginseite)

Wenn es jetzt auf der Seite ein fehlendes Element gibt, dann wird dafĂĽr die 404er geliefert, da da aber ein Login-Formular integriert ist wird das Token erneuert und das "richtige" in der Seite funktioniert nicht mehr.

Uns ist das aufgefallen, da die SourceMaps einiger JS-Files nicht mit deployed waren und der IE11 dafĂĽr dann die startseite.html geladen hatte. Die anderen Browser ignorieren wohl den 404er an der Stelle.

Und nein, das ist vermutlich nicht nur auf den IE beschränkt. Wie schnell kann es durch Unachtsamkeit passieren, dass ein Image im CSS fehlt. Dabei gehe ich mal davon aus, dass auch die anderen dem Link folgen würden und da das selbe passiert. Ich hatte im Chrome auch ein paar mal die CSRF-Fehlermeldung. Bin dem aber damals nicht nachgegangen, da ein reload das behoben hatte.

Volker

bug

Most helpful comment

This is a bug in IE11 (less than 1% market share) that only occurs if you have SourceMaps that redirect to a page with a form on it and happens only if you have the developer tools open.

Since the scope is so narrow, I am for not adding a workaround on our side, too.

All 34 comments

Which Contao version exactly?

LTS 4.9.5

Uns ist das aufgefallen, da die SourceMaps einiger JS-Files nicht mit deployed waren und der IE11 dafĂĽr dann die startseite.html geladen hatte.

So, just to be clear:

  • In your site structure you have configured a 404 page.
  • In the settings of that 404 page you have configured a redirect to a page with the alias startseite.
  • Under a specific front end URL, which also contains a form, missing assets were included.
  • The URL for these assets then generated the 404 page, which redirected to startseite.html.

Question: does startseite.html also include a form?

Did you use a https or an unsecure http-connection?
Can you confirm your Bug with forced https?

In your site structure you have configured a 404 page.

yes

In the settings of that 404 page you have configured a redirect to a page with the alias startseite.

yes

Under a specific front end URL, which also contains a form, missing assets were included.

yes, it was a sourceMap.

The URL for these assets then generated the 404 page, which redirected to startseite.html.

yes

does startseite.html also include a form?

yes, every page contains the login form (or logout)

Did you use a https or an unsecure http-connection?

it's https only. The csfr-cookie is changed

Hm, I am unable to reproduce the problem in Firefox or IE 11. I tried the following reproduction:

  • Create a login form.
  • Integrate login form into your page layout.
  • Create 404 page, enable the redirect setting and choose a page to redirect to.
  • Add a non existent asset, e.g. via an event listener where you execute: $GLOBALS['TL_JAVASCRIPT'][] = 'does/not/exist.js';.
  • Open the front end and try to log in.

According to your description a CSRF token error should occur, but none occurred for me. Are there any other requirements?

ok, i try to create a minimal setup to repoduce.

  • normal CTO 4.9.5
  • create a minimal website mit theme, etc
  • add a login form to the startpage
  • add 404 redirect to that page

-> everything works fine

add a js to the files and link it inside the theme / layout. I used that one to have something "relevant"
`function test (test) {
console.log(test)
}

test('hallo')
//@ sourceMappingURL=jquery.min.map`

The last line does the trick. It forces the IE to do a XMLHttpRequest

image

and yes, login works fine, but logout doesn't in this minimal setup:

image

If you remove / invalidate that line logout work fine.

But you seems to be right. This effect doesn't work with images or CSS. That's good news

I see. Possibly related to #2186 as well? I'll do some further testing when I have the time.

maybe. I'll try the fix

I am able to reproduce the problem now.

  1. Create a login form and a member with login details.
  2. Add login form to the start page via a module content element.
  3. Create 404 page, enable the redirect setting and choose the start page to redirect to.
  4. Create a new template called ce_html_test.html5 with the following content (assuming that foobar is an invalid URL in your setup):
    js <script> var req = new XMLHttpRequest(); req.open("GET", "foobar"); req.send(); </script>
  5. On the start page, create a new HTML content element and choose the ce_html_test template.
  6. Create a new private browsing window and open the start page in the front end.
  7. Try to log in.

The Invalid request token error will occur.

The issue is still not solved. The CSRF-cookie will still be set by an 404 request. You will see the relevant parts of the apache logs. I add the output cookies to it.

This is a simple login / logout flow with just a simple page with a login module

  1. [25/Oct/2020:17:13:42 +0100] "GET /start.html HTTP/1.1" 200 11135 "-" "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko" "Cookie:csrf_contao_csrf_token=TqrqF-MDzL9Hh4a5t50s-OQY6LDMPsCm499azzdIysY; path=/; httponly; samesite=lax"
  2. [25/Oct/2020:17:13:43 +0100] "GET /files/xxx/test.js?v=f78fe00c HTTP/1.1" 200 85 "http://localhost:8080/start.html" "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko" "Cookie:-"
  3. [25/Oct/2020:17:13:43 +0100] "GET /files/test.js.map HTTP/1.1" 404 278505 "-" "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko" "Cookie:-"
  4. [25/Oct/2020:17:14:02 +0100] "POST /start.html HTTP/1.1" 302 374 "http://localhost:8080/start.html" "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko" "Cookie:PHPSESSID=1750a7939f9ae8933e1e93ff8b55ecc1; path=/; httponly, REMEMBERME=deleted; expires=Sat, 26-Oct-2019 16:14:02 GMT; Max-Age=0; path=/; httponly, sf_redirect=%7B%22token%22%3A%22a9022c%22%2C%22route%22%3A%22tl_page.2%22%2C%22method%22%3A%22POST%22%2C%22controller%22%3A%7B%22class%22%3A%22Contao%5C%5CFrontendIndex%22%2C%22method%22%3A%22renderPage%22%2C%22file%22%3A%22%5C%2Fvar%5C%2Fwww%5C%2Fhtml%5C%2Fvendor%5C%2Fcontao%5C%2Fcore-bundle%5C%2Fsrc%5C%2FResources%5C%2Fcontao%5C%2Fcontrollers%5C%2FFrontendIndex.php%22%2C%22line%22%3A86%7D%2C%22status_code%22%3A302%2C%22status_text%22%3A%22Found%22%7D; path=/; httponly; samesite=lax, csrf_contao_csrf_token=57FR5k19nEfH503a6n0VCCnm6O8pgGHQI_Yg54vKads; path=/; httponly; samesite=lax"
  5. [25/Oct/2020:17:14:03 +0100] "GET /start.html HTTP/1.1" 200 11107 "http://localhost:8080/start.html" "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko" "Cookie:sf_redirect=deleted; expires=Sat, 26-Oct-2019 16:14:02 GMT; Max-Age=0; path=/; httponly"
  6. [25/Oct/2020:17:14:03 +0100] "GET /files/xxx/test.js?v=f78fe00c HTTP/1.1" 200 85 "http://localhost:8080/start.html" "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko" "Cookie:-"
  7. [25/Oct/2020:17:14:03 +0100] "GET /files/test.js.map HTTP/1.1" 404 278731 "-" "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko" "Cookie:csrf_contao_csrf_token=_Aa2odq4MOFiwFA-uoRj5H1lbS4eUV8RXR7R-1IWXFI; path=/; httponly; samesite=lax"
  8. [25/Oct/2020:17:14:03 +0100] "GET /favicon.ico HTTP/1.1" 404 - "-" "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko" "Cookie:-"
  9. [25/Oct/2020:17:14:13 +0100] "POST /_contao/logout HTTP/1.1" 400 268955 "http://localhost:8080/start.html" "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko" "Cookie:-"

Line 1 is the first requerst to start.html, which causes a reqeust to test.js & test.js.map. This works fine. After login in (line 4) and redirect to start.html (line 5), test,js & map will be requestet again. This time the request to test.js.map will set a new csrf token which will cause the 400 on logout.

Reloading start.html while being logged in will always generate a new csrf token by requesting the map

My setup is very simple

Contao 4.9.9

  • dummy theme
  • one layout with test.js as external JS (see below)
  • the login module
  • one page (start.html) with just that module
  • and a member

test.js

console.log('here test.js')
//# sourceMappingURL=/files/xxx/test.js.map

This will also happen in 4.10.4.

If i add the 404.redirect page with fixed redirect to start.html, requests will still set cookies

1. [25/Oct/2020:18:12:53 +0100] "GET /start.html HTTP/1.1" 200 11134 "http://localhost:8080/start.html" "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko" "Cookie:csrf_contao_csrf_token=0J48P0Xc8uc2BYKTajpP-o0j0QsmZVHrGqmslr-p9Xc; path=/; httponly; samesite=lax"
2. [25/Oct/2020:18:12:54 +0100] "GET /files/xxx/test.js?v=030f21bd HTTP/1.1" 200 89 "http://localhost:8080/start.html" "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko" "Cookie:-"
3. [25/Oct/2020:18:12:54 +0100] "GET /files/xxx/test.js.map HTTP/1.1" 303 374 "-" "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko" "Cookie:sf_redirect=%7B%22token%22%3A%222efc11%22%2C%22route%22%3A%22tl_page.3.error_404%22%2C%22method%22%3A%22GET%22%2C%22controller%22%3A%7B%22class%22%3A%22Contao%5C%5CFrontendIndex%22%2C%22method%22%3A%22renderPage%22%2C%22file%22%3A%22%5C%2Fvar%5C%2Fwww%5C%2Fhtml%5C%2Fvendor%5C%2Fcontao%5C%2Fcore-bundle%5C%2Fsrc%5C%2FResources%5C%2Fcontao%5C%2Fcontrollers%5C%2FFrontendIndex.php%22%2C%22line%22%3A86%7D%2C%22status_code%22%3A303%2C%22status_text%22%3A%22See%20Other%22%7D; path=/; httponly; samesite=lax, csrf_contao_csrf_token=YMPqu2tSgofg89Tm-H79nD32PY1INOwp_ytTrfkDrnA; path=/; httponly; samesite=lax"
4. [25/Oct/2020:18:12:55 +0100] "GET /start.html HTTP/1.1" 200 11142 "-" "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko" "Cookie:csrf_contao_csrf_token=Ov9IRNU8Umk978PzLxNKIqCyK65YaWelK4T7WmOjXxg; path=/; httponly; samesite=lax"

There will be 3 different cookies set by contao. The first by the original request, the seconde by the map file which will redirect to start.html. The request (line 4) set the third one....

Why do these requests set the cookie? Don’t they get the cookie value in the request headers?

I don't know. Ich have to check by adding it to the log. The map request isnt shown in the network tab, but the page wo which it is redirected:

image

There is no csrf-cookie send but received:

image

No, there ist no cookie send during those requests. Look at the last two lines. These requests request are made during load of "start.html". I also add the location header of each request.

web_1      | 172.28.0.1 - - [26/Oct/2020:00:01:39 +0100] "GET /start.html HTTP/1.1" 200 11138 "-" "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko" "CookieIn:-" "CookieOut:csrf_contao_csrf_token=_8rkQ0tBBjQGHMRUjUuzbWvjXisYsztYXIIEiZ-Lgwc; path=/; httponly; samesite=lax" "Location:-"
web_1      | 172.28.0.1 - - [26/Oct/2020:00:01:40 +0100] "GET /assets/contao/css/layout.min.css?v=89e682c6 HTTP/1.1" 200 421 "http://localhost:8080/start.html" "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko" "CookieIn:csrf_contao_csrf_token=_8rkQ0tBBjQGHMRUjUuzbWvjXisYsztYXIIEiZ-Lgwc" "CookieOut:-" "Location:-"
web_1      | 172.28.0.1 - - [26/Oct/2020:00:01:40 +0100] "GET /assets/contao/css/responsive.min.css?v=89e682c6 HTTP/1.1" 200 315 "http://localhost:8080/start.html" "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko" "CookieIn:csrf_contao_csrf_token=_8rkQ0tBBjQGHMRUjUuzbWvjXisYsztYXIIEiZ-Lgwc" "CookieOut:-" "Location:-"
web_1      | 172.28.0.1 - - [26/Oct/2020:00:01:40 +0100] "GET /files/xxx/test.js?v=030f21bd HTTP/1.1" 200 89 "http://localhost:8080/start.html" "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko" "CookieIn:csrf_contao_csrf_token=_8rkQ0tBBjQGHMRUjUuzbWvjXisYsztYXIIEiZ-Lgwc" "CookieOut:-" "Location:-"
web_1      | 172.28.0.1 - - [26/Oct/2020:00:01:40 +0100] "GET /_wdt/cbc3c5 HTTP/1.1" 200 4287 "http://localhost:8080/start.html" "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko" "CookieIn:csrf_contao_csrf_token=_8rkQ0tBBjQGHMRUjUuzbWvjXisYsztYXIIEiZ-Lgwc" "CookieOut:csrf_contao_csrf_token=deleted; expires=Sat, 26-Oct-2019 23:01:40 GMT; Max-Age=0; path=/; httponly" "Location:-"
web_1      | 172.28.0.1 - - [26/Oct/2020:00:01:40 +0100] "GET /favicon.ico HTTP/1.1" 404 - "-" "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko" "CookieIn:csrf_contao_csrf_token=_8rkQ0tBBjQGHMRUjUuzbWvjXisYsztYXIIEiZ-Lgwc" "CookieOut:-" "Location:-"
web_1      | 172.28.0.1 - - [26/Oct/2020:00:01:40 +0100] "GET /files/xxx/test.js.map HTTP/1.1" 303 374 "-" "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko" "CookieIn:-" "CookieOut:sf_redirect=%7B%22token%22%3A%22c5d8cc%22%2C%22route%22%3A%22tl_page.3.error_404%22%2C%22method%22%3A%22GET%22%2C%22controller%22%3A%7B%22class%22%3A%22Contao%5C%5CFrontendIndex%22%2C%22method%22%3A%22renderPage%22%2C%22file%22%3A%22%5C%2Fvar%5C%2Fwww%5C%2Fhtml%5C%2Fvendor%5C%2Fcontao%5C%2Fcore-bundle%5C%2Fsrc%5C%2FResources%5C%2Fcontao%5C%2Fcontrollers%5C%2FFrontendIndex.php%22%2C%22line%22%3A86%7D%2C%22status_code%22%3A303%2C%22status_text%22%3A%22See%20Other%22%7D; path=/; httponly; samesite=lax, csrf_contao_csrf_token=1aIJkLOYcPF71TCAroJeiqMK1El1j7v7d_6RBKoYV6k; path=/; httponly; samesite=lax" "Location:http://localhost:8080/start.html"
web_1      | 172.28.0.1 - - [26/Oct/2020:00:01:42 +0100] "GET /start.html HTTP/1.1" 200 11136 "-" "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko" "CookieIn:-" "CookieOut:csrf_contao_csrf_token=vW7BmY5kjk2jpXpsz0YnQlfL8VW_s85jN1YpCecLO4c; path=/; httponly; samesite=lax" "Location:-"

I don't know. Ich have to check by adding it to the log. The map request isnt shown in the network tab, but the page wo which it is redirected:

image

There is no csrf-cookie send but received:

image

you are using http (unsecure connection).
Contao with isotope is not compatible with unsecure connections.
Either you enforce https or add these line

Would be nice to know, if it fix your bug.

yea, this is http. We saw this issue an https also, but i can retest it, just to be sure

This will also happend using https

image

image

Can someone reopen that issue?

Danke Leo!

I can provide a demo system, if useful

Contao 4.9.9

  • dummy theme
  • one layout with test.js as external JS (see below)
  • the login module
  • one page (start.html) with just that module
  • and a member

test.js

console.log('here test.js')
//# sourceMappingURL=/files/xxx/test.js.map

I was unable to reproduce the problem in Contao 4.9.9. Any kind of 404 response (be it from a missing favicon.ico or a missing sourceMapping file) does not interfere with the request token for the form of the current page.

Did you use IE11? Adding a 404 rediekt to the start page will force the error at login. In the upper description it will happend during logout. I can still provide a test installation

Did you use IE11?

No, Firefox and Chrome. But I tried with IE11 just now and also was unable to reproduce the problem

Adding a 404 rediekt to the start page will force the error at login.

Ah ok, that was not in your original reproduction steps. However, I just tried with enabling the forwarding to the home page in the 404 page and still was not able to reproduce the problem in either of the aforementioned browsers.

I can still provide a test installation

Sure :). How would you be able to provide it?

What about a SQL-dump?

I think it would be better if you hosted your test installation some where (and reproduce the problem there), so that we can rule out any environment differences.

ok. I'll be back with an URL

ok. I set up an example site.

https://cto2246.extranet.addmore.cloud/

Steps to reproduce:

to enter contao backend use "admin" / "admin123"

Yes, I can reproduce the problem there 👍

So the request to /files/xxx/test.js.map doesn’t include the CSRF cookie (only the PHPSESSID one). I think this is a bug in IE11.

@volkerrichert why does /files/xxx/test.js.map redirect to /start.html? If /files/xxx/test.js.map would return a 404 response instead, the issue would not occur I think.

@volkerrichert why does /files/xxx/test.js.map redirect to /start.html? If /files/xxx/test.js.map would return a 404 response instead, the issue would not occur I think.

In Contao you can enable a redirect for the 404 page. The response to /start.html then returns a new CSRF token in its response, because /start.html contains a form.

yes, it's like fritz said.

The only “fix” I can think of would be to check in_array('text/html', $request->getAcceptableContentTypes()) to set the cookie only on “main” requests.

But this could be buggy and we would have to check for application/x-ms-application too if we want to support IE8 (because it doesn’t send text/html).

This is a bug in IE11 (less than 1% market share) that only occurs if you have SourceMaps that redirect to a page with a form on it and happens only if you have the developer tools open.

I think we should close this bug or debug further why IE11 only sends the PHPSESSID cookie and not the CSRF cookie.

This is a bug in IE11 (less than 1% market share) that only occurs if you have SourceMaps that redirect to a page with a form on it and happens only if you have the developer tools open.

Since the scope is so narrow, I am for not adding a workaround on our side, too.

Was this page helpful?
0 / 5 - 0 ratings