开发者

How to find user id from session_data from django_session table?

In django_session table session_data is stored which is first pickled using pickle module of Python and then encoded in base64 by using base64 module of Python.

I got the decoded pickled session_data.

session_data from django_session table:

gAJ9cQEoVQ9fc2Vzc2lvbl9leHBpcnlxAksAVRJfYXV0aF91c2VyX2JhY2tlbmRxA1UpZGphbmdvLmNvbnRyaWIuYXV0aC5iYWNrZW5kcy5Nb2RlbEJhY2tlbmRxBFUNX2F1dGhfdXNlcl9pZHEFigECdS5iZmUwOWExOWI0YTZkN2M0NDc2MWVjZjQ5ZDU0YjNhZA==

after decoding it by base64.decode(session_data):

 \x80\x02}q\x01(U\x0f_session_expiryq\x02开发者_StackOverflow社区K\x00U\x12_auth_user_backendq\x03U)django.contrib.auth.backends.ModelBackendq\x04U\r_auth_user_idq\x05\x8a\x01\x02u.bfe09a19b4a6d7c44761ecf49d54b3ad

I want to find out the value of auth_user_id from auth_user_idq\x05\x8a\x01\x02u.


I had trouble with Paulo's method (see my comment on his answer), so I ended up using this method from a scottbarnham.com blog post:

from django.contrib.sessions.models import Session
from django.contrib.auth.models import User

session_key = '8cae76c505f15432b48c8292a7dd0e54'

session = Session.objects.get(session_key=session_key)
uid = session.get_decoded().get('_auth_user_id')
user = User.objects.get(pk=uid)

print user.username, user.get_full_name(), user.email


NOTE: format changed since original answer, for 1.4 and above see the update below

import pickle

data = pickle.loads(base64.decode(session_data))

>>> print data
{'_auth_user_id': 2L, '_auth_user_backend': 'django.contrib.auth.backends.ModelBackend',
 '_session_expiry': 0}

[update]

My base64.decode requires filename arguments, so then I tried base64.b64decode, but this returned "IndexError: list assignment index out of range".

I really don't know why I used the base64 module, I guess because the question featured it.

You can just use the str.decode method:

>>> pickle.loads(session_data.decode('base64'))
{'_auth_user_id': 2L, '_auth_user_backend': 'django.contrib.auth.backends.ModelBackend',
 '_session_expiry': 0}

I found a work-around (see answer below), but I am curious why this doesn't work.

Loading pickled data from user sources (cookies) is a security risk, so the session_data format was changed since this question was answered (I should go after the specific issue in Django's bug tracker and link it here, but my pomodoro break is gone).

The format now (since Django 1.4) is "hash:json-object" where the first 40 byte hash is a crypto-signature and the rest is a JSON payload. For now you can ignore the hash (it allows checking if the data was not tampered by some cookie hacker).

>>> json.loads(session_data.decode('base64')[41:])
{u'_auth_user_backend': u'django.contrib.auth.backends.ModelBackend',
 u'_auth_user_id': 1}


If you want to learn more about it and know how does encode or decode work, there are some relevant code. By the way the version of Django that i use is 1.9.4.

django/contrib/sessions/backends/base.py

class SessionBase(object):
    def _hash(self, value):
        key_salt = "django.contrib.sessions" + self.__class__.__name__
        return salted_hmac(key_salt, value).hexdigest()
    def encode(self, session_dict):
        "Returns the given session dictionary serialized and encoded as a string."
        serialized = self.serializer().dumps(session_dict)
        hash = self._hash(serialized)
        return base64.b64encode(hash.encode() + b":" + serialized).decode('ascii')
    def decode(self, session_data):
        encoded_data = base64.b64decode(force_bytes(session_data))
        try:
            # could produce ValueError if there is no ':'
            hash, serialized = encoded_data.split(b':', 1)
            expected_hash = self._hash(serialized)
            if not constant_time_compare(hash.decode(), expected_hash):
                raise SuspiciousSession("Session data corrupted")
            else:
                return self.serializer().loads(serialized)
        except Exception as e:
            # ValueError, SuspiciousOperation, unpickling exceptions. If any of
            # these happen, just return an empty dictionary (an empty session).
            if isinstance(e, SuspiciousOperation):
                logger = logging.getLogger('django.security.%s' %
                        e.__class__.__name__)
                logger.warning(force_text(e))
            return {}

django/contrib/sessions/serializer.py

class JSONSerializer(object):
    """
    Simple wrapper around json to be used in signing.dumps and
    signing.loads.
    """
    def dumps(self, obj):
        return json.dumps(obj, separators=(',', ':')).encode('latin-1')
    def loads(self, data):
        return json.loads(data.decode('latin-1'))

Let's focus on SessionBase's encode function.

  1. Serialize the session dictionary to a json
  2. create a hash salt
  3. add the salt to serialized session , base64 the concatenation

So, decode is inverse. We can simplify the decode function in the following code.

import json
import base64
session_data = 'YTUyYzY1MjUxNzE4MzMxZjNjODFiNjZmZmZmMzhhNmM2NWQzMTllMTp7ImNvdW50Ijo0fQ=='
encoded_data = base64.b64decode(session_data)
hash, serialized = encoded_data.split(b':', 1)
json.loads(serialized.decode('latin-1'))

And that what session.get_decoded() did.


from django.conf import settings
from django.contrib.auth.models import User
from django.utils.importlib import import_module        

def get_user_from_sid(session_key):
    django_session_engine = import_module(settings.SESSION_ENGINE)
    session = django_session_engine.SessionStore(session_key)
    uid = session.get('_auth_user_id')
    return User.objects.get(id=uid)


I wanted to do this in pure Python with the latest version of DJango (2.05). This is what I did:

>>> import base64
>>> x = base64.b64decode('OWNkOGQxYjg4NzlkN2ZhOTc2NmU1ODY0NWMzZmQ4YjdhMzM4OTJhNjp7Im51bV92aXNpdHMiOjJ9')
>>> print(x)
b'9cd8d1b8879d7fa9766e58645c3fd8b7a33892a6:{"num_visits":2}'
>>> import json
>>> data = json.loads(x[41:])
>>> print(data)
{'num_visits': 2}


I just had to solve something like this on a Django install. I knew the ID (36) of the user and wanted to delete the session data for that specific user. I wanted to put this code out as a prototype to build from for finding a user in session data:

from django.contrib.sessions.models import Session

TARGET_USER = 36  # edit this to match  target user.

TARGET_USER = str(TARGET_USER)  # type found to be a string

for session in Session.objects.all():
    raw_session= session.get_decoded()
    uid = session.get_decoded().get('_auth_user_id')
    if uid == TARGET_USER:  # this could be a list also if multiple users
        print(session)
        # session.delete()  # uncomment to delete session data associated with the user

Hope this helps anyone out there.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜