Framework: Cookie session driver causes issues with SameSite strict or lax

Created on 12 Feb 2020  Â·  8Comments  Â·  Source: laravel/framework

  • Laravel Version: 6.15.0
  • PHP Version: 7.4.0
  • Session Driver: cookie
  • Chrome 80

Description:

Using the cookie-setting SameSite=strict causes links from other domains to not send cookies on the first request. This causes Laravel to create a new session with a new identifier, but the old session data cookie is left, and passed on every request.

Follow a few links like that (or reload the page after following a link) and the number of cookies increases, until the server will start rejecting the requests due to the header size (exactly when depends on the server settings, but around 10 times is enough for me).

image

400 Bad Request (Request Header Or Cookie Too Large)

Steps To Reproduce:

  • Create a brand new Laravel application
  • Set SESSION_DRIVER=cookie in .env
  • Set 'same_site' => 'strict' in config/session.php
  • Create a link to your page from another domain, and follow that a few times
  • See the number of cookies increase for every request
  • There is no need to store any manual data in the session, the default is sufficient to cause a problem

Lax

A very similar issue exists for the 'lax'-setting, which I suppose is more common (and default in Chrome 80). Normal links are fine, but POST-requests to your page does not include cookies and will cause the same issue.

Additional information

I do understand that the same problem has always existed if the laravel_session cookie, which points to the actual session data cookie, is deleted and the page is reloaded. The difference here is that this issue affects users unwittingly and that browser defaults have changed.

Ideas

I have a very brief understanding of how the session cookies function in Laravel and how they are set, and I do understand that it is impossible to clean up data after the pointer is lost. However, one solution could be to name the data-cookie in a specific way, for instance laravel_session_data_SESSIONID, instead of just SESSIONID, such that dangling data can be deleted, or to always use the same cookie name for the data, since the data store is not shared between users.

I guess the reason that laravel_session is just a pointer is to be more generic and behave the same way for all session drivers, but in the case of cookies, couldn't the data be stored directly on that cookie (saving data on every request)?

needs more info

Most helpful comment

@taylorotwell I was able to reproduce both issues at nova.laravel.com, which uses the cookie driver.

Steps:

  • Force someone to make a post-redirect to nova.laravel.com, e.g. by embedding a simple script at any site the user might visit (yes that's a big if, I realize that).
  • The CSRF-protection will respond with 419, but the user is logged out, and the old cookie is kept (for reasons stated above). Do this a few times, and nova.laravel.com doesn't work until cookies are cleared.

Since nova.laravel.com doesn't specify any SameSite-value, it relies on the default. This will be lax in future Chrome, and this new setting can be forced today by visiting chrome://flags and searching for 'lax'.

Chrome allows lax-cookies to be sent in a post-request for 2 minutes after creation, but after waiting these two minutes, the "attack" is successful.

image

image

It's not a big problem, I agree, just wanted to clarify that it has nothing to do with bad setup of Laravel (except for choice of session-driver).

All 8 comments

I'm not sure where all those cookies come from. Unfortunately I don't have time to deep dive into this so appreciating it if anyone can help out here.

Thanks for responding.

A cookie with the session data is always created when the cookie driver is used. The SameSite setting strict or lax may cause the cookie to not be sent, even though it exists and is kept in the browser for future requests. Laravel has no way of knowing this and responds with a new data-cookie (with a new random name).

If no cookies aren't sent then Laravel will not have any way to determine anything about old sessions. There is likely no solution to this and the answer is simply to use a different session driver such as database.

Personally I suggest using "lax". It would not be very typical for other sites to be making full POSTs to your domain anyways.

I agree @taylorotwell about 'lax'. It's quite disturbing though that a post-request to any site from anywhere causes the user on that site to be logged out (regardless of session driver), since Laravel can't see the session and generates a new.

I'll see if I can try out my ideas about the original problem some day. Here's three ideas to try if anyone else wants to look at it:

  • Always use same cookie name for session-data, which will overwrite instead of keep the old dangling.

    • Use a prefix for data cookies and clear old unused (the above is probably a better idea).

    • Put the session data directly in the laravel_session cookie. This would be nice, but will probably be harder to implement and add some special cases to the session handling.

@taylorotwell I was able to reproduce both issues at nova.laravel.com, which uses the cookie driver.

Steps:

  • Force someone to make a post-redirect to nova.laravel.com, e.g. by embedding a simple script at any site the user might visit (yes that's a big if, I realize that).
  • The CSRF-protection will respond with 419, but the user is logged out, and the old cookie is kept (for reasons stated above). Do this a few times, and nova.laravel.com doesn't work until cookies are cleared.

Since nova.laravel.com doesn't specify any SameSite-value, it relies on the default. This will be lax in future Chrome, and this new setting can be forced today by visiting chrome://flags and searching for 'lax'.

Chrome allows lax-cookies to be sent in a post-request for 2 minutes after creation, but after waiting these two minutes, the "attack" is successful.

image

image

It's not a big problem, I agree, just wanted to clarify that it has nothing to do with bad setup of Laravel (except for choice of session-driver).

I'm not sure what you want me to do. This is simply how cookies work with these settings 😄 ... if you don't like that behavior use a dfferent session driver.

Just to clarify, using "Strict" causes issues regardless of session driver, since all drivers keep id in a cookie.

For "lax", i'll think some more about my proposed solutions above, but I'm sure someone else will figure something better out :)

@taylorotwell Regarding your previous comment: _"It would not be very typical for other sites to be making full POSTs to your domain anyways."_

I agree, but today I realized that this is quite common using OAuth (access_token is sent back in a POST request). The solution is thankfully simple – to use an endpoint which does not use the StartSession middleware.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

JamborJan picture JamborJan  Â·  3Comments

shopblocks picture shopblocks  Â·  3Comments

digirew picture digirew  Â·  3Comments

Fuzzyma picture Fuzzyma  Â·  3Comments

YannPl picture YannPl  Â·  3Comments