Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Race condition on logout, session alive again #328

Closed
0x0ece opened this issue Jun 10, 2021 · 9 comments · Fixed by #333
Closed

Race condition on logout, session alive again #328

0x0ece opened this issue Jun 10, 2021 · 9 comments · Fixed by #333
Assignees
Labels

Comments

@0x0ece
Copy link

0x0ece commented Jun 10, 2021

Hi, currently express-session + connect-redis suffer from a race condition on logout:

  1. ReqA starts and maybe modifies the session
  2. ReqB arrives and logs out
  3. ReqA terminates after ReqB and overrides the del by readding the session in redis

For context we've been working on (what we think is) a more secure fork of express-session, and also worked on a similar fix for connect-typeorm. The issue with redis came up discussing with a user interested in our fork, but using connect-redis.

TBH I'm not sure what the best solution would be with redis. IMO the real issue is express-session that offers a generic set instead of an explicit insert vs update, but it what it is... maybe an opportunity is to have a better/richer interface for stores.

If you have ideas, happy to work on a PR.

P.S: and big congrats on having 0 open issues, pretty rare!

Edit: renamed the package to express-session-jwt, so fixed the link above

@wavded wavded self-assigned this Jun 10, 2021
@wavded
Copy link
Collaborator

wavded commented Jun 10, 2021

This has always been a limitation of connect-redis (and other session storage implementations). Of course, this affects not just destroying sessions but also setting them. However, given the high use of this library, the issue has been seldom reported in my experience. Note that sessions have a TTL, which can be configured. If for some reason the session was recreated, it would be cleaned up by the TTL set on the keys when that time expired.

I don't know of a way we can account for concurrent session requests racing. On session deletion, the keys are gone by design. It seems we'd have to keep them around just in case something happens right away and try to clean them up but the rules around that don't seem clear IMO.

That my 2 cents.

I see it important to match connect-redis API with your implementation, but I have a feeling this issue exists in more stores. Have you any ideas on what you'd change in the express-session API? Any luck in discussing this with the maintainers there?

@0x0ece
Copy link
Author

0x0ece commented Jun 11, 2021

I read the past issues and it's clear that there's a race condition on concurrent updates. That's, however, an issue that only the application can solve, and not really a problem just with sessions.

Destroy is different because the user logs out and the session becomes alive again. Think you're on facebook, look at the list of your sessions, click destroy on one that you don't recognize... and that's not really destroyed.

I think there are 2 solutions here:

  1. Soft delete. That's easier on SQL-like stores than noSQL. You add a column destroyedAt = NULL by default, and rewrite the update query as UPDATE ... WHERE destroyedAt is NULL. In the memory store I kept a map of destroyed sessions and rewrote the updates as "if not destroyed, then update". In redis TBH I don't know what the most efficient solution would be.

  2. Add insert, update to the store api, instead of just set. In redis, these could be implemented with set nx, set xx. However this won't work with the original express-session, unless they also change interface. In this case the session flow would be calling the insert on creation, the update on touch & update. The destroy would delete, but the update wouldn't re-insert.

@wavded
Copy link
Collaborator

wavded commented Jun 15, 2021

Hey @0x0ece , just acknowledging that I have read your comment and am still thinking about this. Part of the "perk" of this session storage library is we don't keep any sessions around after they are destroyed. We keep a low footprint and we stay fast. I don't love keeping session objects alive and adding a "cleanup" task in case sessions get quickly recreated as it adds more overhead to the process. It seems like your idea of differentiating from an insert versus an update may get around this?

I noticed your fork no longer exists, did this get moved? Ideally, any API change would be something that would get integrated into the main express-session project.

@0x0ece
Copy link
Author

0x0ece commented Jun 17, 2021

Hi, sorry for the broken link, we decided to rename it to express-session-jwt. Also, published the blog post.

I agree, reaching out to express-session might be the best solution. We could quickly prototype the new insert+update interface within express-session-jwt, provided they're interesting in supporting the new api as well. I'll consolidate the message and open an issue there.

@github-actions
Copy link

This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.

@github-actions github-actions bot added the stale label Jul 18, 2021
@wavded
Copy link
Collaborator

wavded commented Jul 19, 2021

I see you have a conversation started with @dougwilson on this matter. I will keep this case open to see if anything comes out that that may apply to this.

@wavded wavded removed the stale label Jul 19, 2021
@github-actions
Copy link

This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.

@github-actions github-actions bot added the stale label Aug 19, 2021
@github-actions
Copy link

This issue was closed because it has been stalled for 5 days with no activity.

wavded pushed a commit that referenced this issue Oct 15, 2021
This fixes an issue where multiple requests modifying the session are in progress at the same time, before this fix the last one to complete looses the changes made by the other in flight request. After this fix changes are merged in from all in flight requests.

#328
jellis24 added a commit to jellis24/connect-redis that referenced this issue Apr 6, 2022
This fixes an issue where multiple requests modifying the session are in progress at the same time, before this fix the last one to complete looses the changes made by the other in flight request. After this fix changes are merged in from all in flight requests.

tj/connect-redis#328
@HughePaul
Copy link

If this does get re-added it should probably be an optional feature, as merging various session states can break apps in other ways.
Possibly the app should be given the opportunity to decide what to do with two conflicting sessions.
Quite often an app would only care about if a user is logged out in either session update, has paid in either session update, or has been given something already.

Particularly, deepmerge concatenates arrays by default since v2, which can break all sorts of things, like causing the session size to spiral exponentially.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants