8

I recently wrote shopping cart code that depends on the Session object. It seemed the reasonable way to store data for anonymous users.

While doing a bunch of testing, I ran into an annoying problem--when users sign in part way through the checkout process (or simply while browsing for other products), Django issues a new session_key and I lose access to my session data.

Is there a way to keep the old session data? Or is my design approach wrong?

Aaron C. de Bruyn
  • 2,347
  • 1
  • 30
  • 40

3 Answers3

3

Try writing your own SessionBackend that inherits from existing one and overrides the cycle_key method.

1 In your settings.py:

SESSION_ENGINE = 'my_app.session_backend'

2 my_app.session_backend.py:

from django.contrib.sessions.backends.db import SessionStore as DbSessionStore

class SessionStore(DbSessionStore):
    def cycle_key(self):
        pass

cycle_key is beeing called in login view after authentication.

Let me now if it works ;)

danpalmer
  • 2,163
  • 4
  • 24
  • 41
rafek
  • 635
  • 5
  • 8
  • It works! I still need to spend some time digging through the Django code to figure out if there are any security implications related to not cycling the key, but I figure it should be fairly easy to do some more work in the cycle_key function to simply re-associate cart data with the new key. – Aaron C. de Bruyn Aug 25 '12 at 18:28
  • 7
    Apparently I can't add styled code to comments. Here's my slightly expanded solution. Thanks rafek! https://gist.github.com/3473470 – Aaron C. de Bruyn Aug 26 '12 at 03:07
  • 2
    -1. This may solve the problem, but disabling a security mechanism is not how you should do things. See @AaronC.deBruyn's solution for a better way to do it. – intgr Aug 29 '14 at 08:45
  • I ended up modifying @rafek's example. Instead of just passing, I generate a new key, re-associate the data from the old key, and continue on with the new session key. – Aaron C. de Bruyn Aug 31 '14 at 02:55
  • 4
    This introduces a security vulnerability - Django specifically cycles the keys to mitigate session fixation attacks. Django documentation: https://docs.djangoproject.com/en/1.8/topics/http/sessions/#django.contrib.sessions.backends.base.SessionBase.cycle_key - OWASP documentation: https://www.owasp.org/index.php/Session_fixation – danpalmer Aug 26 '15 at 11:40
  • 1
    @AaronC.deBruyn: Please go ahead and post that GitHub snippet as an answer. Answering your questions is encouraged. – Flimm Jan 14 '16 at 18:18
  • @rafek pointed me in the right direction. I think he deserves the credit. – Aaron C. de Bruyn Jan 14 '16 at 21:10
  • 2
    @AaronC.deBruyn, apologies for necromancy—trying to understand your solution, do you forget to call `super().cycle_key()` in your exception handler, or do you avoid that on purpose? (talking about https://gist.github.com/darkpixel/3473470) – Anton Strogonoff May 22 '17 at 07:00
  • 1
    No worries @tony. It's been a while since I touched that project, but I think I should be cycling the key when the exception occurs--after all there is no cart at that point, so I don't care if we lose association with it. :) Good luck on your project, and nice catch! – Aaron C. de Bruyn May 24 '17 at 00:06
1

Instead of disabling the cycle_key() (which is a security measure to avoid session fixation vulnerabilities), you could consider restoring the values through a decorator at the login and logout views. See:

https://stackoverflow.com/a/41849076/146289

Community
  • 1
  • 1
vdboor
  • 21,914
  • 12
  • 83
  • 96
0

I'm trying to do something similar. Django can change the session_key to mitigate session fixation vulnerabilities, so it's not suitable for a foreign key. I want something more permanent. So I'll just put the permanent identifier in request.session['visitor_id']:

from django.utils.crypto import get_random_string
import string

VALID_KEY_CHARS = string.ascii_lowercase + string.digits

def example_view(request):
    if not request.session.get('visitor_id'):
        self.request.session['visitor_id'] = get_random_string(32, VALID_KEY_CHARS)
    # Now code the rest of the view, using the visitor_id instead of
    # session_key for keys in your model.
    # ...
Flimm
  • 136,138
  • 45
  • 251
  • 267