sessions: Race condition in FilesystemStore
There is a race condition in FilesystemStore
that I intend to fix but I would like your input before I go ahead and do it. Basically the problem is that if you have concurrent requests from the same user (same session) that the following is possible:
- Request 1 opens the session to perform a semi long operation
- Request 2 opens the session
- Request 2 Removes session data to perform “logout” or similar
- Request 2 saves
- Request 1 saves, which makes it as if the session was never logged out
I have added a test case for this flaw at cless/sessions@f84abeda17de0b4fcd72d277412f3d3192f206f2
The most straight forward way to fix this would be by introducing locks at the file system level. However, golang has no cross platform way to do file locking. It does expose flock
in syscall
but that only works if the OS supports it. I believe the behavior of flock might also be different on different unixes although I am not sure that this is the case. Another issue with flock is that it might not work on NFS.
An entirely different solution would be to keep a map of locks in the FilesystemStore
object itself. This has another set of disadvantages: You can’t have multiple processes access the same file system sessions and you can’t create multiple stores for the same file system session within a single application. However, both these things are already impossible to do without causing issues.
In the end, I think the best solution is to keep a map of locks in the store object because all the disadvantages in that scenario can be properly documented and you can reply on the behavior being the same across different systems.
Other storage backends that are based on FilesystemStore might copy this flaw (I noticed this issue when reviewing Redistore code for a project of mine boj/redistore#2)
About this issue
- Original URL
- State: closed
- Created 11 years ago
- Reactions: 1
- Comments: 24 (13 by maintainers)
What is the situation on this?
“we could provide a default implementation using in-memory locks”
This would be meaningless in a multi-server environment where requests are load balanced.
There’s a good article about the nuances of per-session locking here:
http://thwartedefforts.org/2006/11/11/race-conditions-with-ajax-and-php-sessions/
As we’ve already concluded here the timeout for locks if something goes wrong is one of the major issues implementing this kind of scheme. This needs more thought and is very backend dependant
As far as implementing Lock/Release we could make the per-session lock implementation optional by having a separate interface. The backend could then be asserted against that interface and if it does not implement it we could provide a default implementation using in-memory locks.
@cless Good points.
I like your proposed interface changes.
Your lock expiration would be relatively easy to implement for RediStore, Redis has an EXPIRE command which allows a TTL to be set for a key.
here are some real world examples of the impact of session race conditions. The bugs are hard to debug, show up seemingly at random and are an annoyance for both developers and users. Given a sufficiently large application that makes extensive use of ajax these race conditions will show up sooner or later. http://www.hiretheworld.com/blog/tech-blog/codeigniter-session-race-conditions http://www.chipmunkninja.com/Troubles-with-Asynchronous-Ajax-Requests-g@
I’ve read the whole thread at EllisLab/CodeIgniter#1746 which deals with a similar issue, but their issue is complicated by the fact that CodeIgniter forcfully regenerates the session id every once in a while and most of the discussion there revolves around this fact.
I understand your reluctance to change sessions in an incompatible way or to introduce subtle differences in the API that might break existing applications. However, I still think there should be some optional locking involved. What about adding two functions to the store API like this:
Use of these functions would be entirely optional (although I would personally encourage locking in every request that is going to write to the session) and have no impact on existing applications.
There is an issue with the proposed locking interface: if the lock is held in a database such as MySQL and your database connection drops after you acquire the lock then you can never release it and your session is effectively deadlocked. Locks should expire in some way, but I am not entirely sure how that should be exposed to the developer.
The default Django session backend is the database and this uses the Django database abstraction layer which I am unfamiliar with so it is hard for me to interpret. However, I found this comment in the filesystem backend:
This seems to suggest they make sure the session file contents are never mangled but not that no race conditions can happen. If anyone has Django experience it would be nice to see a test case like cless/sessions@f84abed Stackoverflow also seems to indicate Django might have race conditions in the sessions: http://stackoverflow.com/search?q=django+session+race+condition