Thank you for your suggestion about using samesite=strict! I'll look into it a bit more - the only downside I see is support for older browsers, which is a personal vice I can't kick. If I see that there's no elegant CSRF solution, this will probably be my fire escape. Thank you for that!
I added more info in the document regarding cookie/session management, particularly its expiry (https://github.com/fpereiro/backendlore#identity--security). The approach I take is to make cookies last until the server says so. I'm very curious as to how you use cookies with varying expiration times and would love to know more if you have the time.
Seconding @fpereio's request - I too would like to know more. CSRF handling is painful, so your approach is very interesting, but I don't understand it fully yet.
Until several years ago cookies couldn't be used for CSRF tokens, because they were sent with every request to your site, regardless of where the request originated. SameSite changes that because you can now control when a given cookie is sent with a given request, based on how a link was followed. It puts the work on the browser.
SameSite is just a property you use when setting a cookie, like the expiration, or url path. There are three settable values: None, Lax, and Strict.
'None' sends the cookie with every
request, like how browsers "historically" acted.
'Lax' is the new default [1]. If someone follows a link to your page, these cookie are sent with the request. If a third-party site POSTs to one of your pages, these cookies won't be sent.
'Strict' cookies will never be sent to your server if the request originated from a third party, even if it's as innocent as following a link from a search engine.
So use these types to your liking. Your needs vary. If you want the user to appear logged in when they follow a link to your site you're going to use None or (most likely) Lax.
As far as CSRF, you can generate a single CSRF token at login and set it in a cookie as with SameSite=Strict. Regenerate it every so often, or at "big" events, and voila. One CSRF token per device session. No worries about multiple tabs open, and you have reasonable expectation to only have to keep track of a single token at a time. The kicker is, you technically don't even have to have a CSRF token if you don't want. If you're serious about nobody linking to an "internal" authenticated page, just set the primary session id cookie as strict. (I would personally do more than this, but hey.)
I do secure portals. So we set several cookies across the board. Varying expiration times. They all have their purposes.
[1] While you can consider Lax as the new default, if you omit the SameSite property on a cookie it is actually attributed as something referred to as 'NotSet', which is unsettable, and acts a hybrid between Lax and None. Browsers vary a little here, but it's only to maintain reasonable backwards compatibility at the moment, and both Mozilla and Chrome developers plan to remove this hybrid functionality in the future. Basically.. a cookie will act like None for 2 minutes, then act like Lax after that, etc.
Since I want to support as many browsers as possible, I'm going to still use CSRF tokens, but a single token per session as you suggested. I just outlined the approach here: https://news.ycombinator.com/item?id=22268152 .
Thanks for your detailed feedback, it's very valuable to me.
I added more info in the document regarding cookie/session management, particularly its expiry (https://github.com/fpereiro/backendlore#identity--security). The approach I take is to make cookies last until the server says so. I'm very curious as to how you use cookies with varying expiration times and would love to know more if you have the time.