Session Data Race Condition Leading to Dropped Sessions
|Reported by:||simonb||Owned by:||mtredinnick|
|Has patch:||yes||Needs documentation:||no|
|Needs tests:||no||Patch needs improvement:||yes|
SESSION_SAVE_EVERY_REQUEST = True
SESSION_ENGINE = 'django.contrib.sessions.backends.file'
With the settings as above we observe that the session cookie changes on every request.
The save() method calls the load() method after it has truncated the file.
The SessionStore.save() method writes back the cached session dict to storage. If the session dict (SessionBase._session_cache) is not already cached, a cache is attempted via accessing the session._session attribute. That is, for uncached sessions the save() method will (indirectly) call the load method. The problem (in the file backend) is that this happens after the session file store has been open with 'wb' - i.e. truncated. So it loads an empty file, triggering a SuspiciousOperation exception form SessionBase.decode() which cause a new session key to be generated and sent, which means a new file and hence all the session data gone.
Make sure the session is cached before the store is written in the save method. One way to do this is in the attached patch.
This was a devil to track down. My client's log in sessions where getting dropped randomly. I set up a duplicate test server and (of course) the problem did not manifest itself. I instrumented the code with debug logging - the problem went away. Don't you just love it when that happens. It does at least point to a race/timing problem. Finally, I inserted flocks around access to the session file store. This lead to the response hanging as the load method (called via save()) could never lock the file since the save() method had it locked and was not finished with it yet.
I was using the database as session storage and seeing the same errors. I switched to the file backend to try to track the problem. So this problem seems to be endemic to the session backends. Though it is much easier to track on the file backend. It is also, because it is a timing issue, dependant on the server setup hw/sw so it may not manifest everywhere.
The attached patch also corrects a spelling error in the comments of the Session middleware
This error message: raise SuspiciousOperation("User tampered with session cookie.") from sessions/backends/base.py is mis-leading. Actually the exposed tamper is with the session data as stored on the server not with the cookie per se. Of course, to do that someone would have to have access to the session store. Perhaps a better message would be "Invalid session data"
Change History (10)
Changed 6 years ago by simonb
comment:1 Changed 6 years ago by Simon Greenhill
- Needs documentation unset
- Needs tests unset
- Patch needs improvement unset
- Triage Stage changed from Unreviewed to Ready for checkin
comment:4 Changed 6 years ago by anonymous
- Patch needs improvement set
- Triage Stage changed from Ready for checkin to Accepted
Changed 6 years ago by mrts
comment:7 Changed 6 years ago by mtredinnick
- Resolution set to fixed
- Status changed from new to closed