如何从django_session表的session_data中找到用户ID?
在 django_session
表中,session_data
是存储的会话数据。这个数据首先是用 Python 的 pickle 模块进行序列化的,然后再用 Python 的 base64 模块进行编码。
我已经得到了被解码的 session_data
。
从 django_session
表中提取的 session_data
是:
gAJ9cQEoVQ9fc2Vzc2lvbl9leHBpcnlxAksAVRJfYXV0aF91c2VyX2JhY2tlbmRxA1UpZGphbmdvLmNvbnRyaWIuYXV0aC5iYWNrZW5kcy5Nb2RlbEJhY2tlbmRxBFUNX2F1dGhfdXNlcl9pZHEFigECdS5iZmUwOWExOWI0YTZkN2M0NDc2MWVjZjQ5ZDU0YjNhZA==
通过 base64.decode(session_data) 解码后得到:
\x80\x02}q\x01(U\x0f_session_expiryq\x02K\x00U\x12_auth_user_backendq\x03U)django.contrib.auth.backends.ModelBackendq\x04U\r_auth_user_idq\x05\x8a\x01\x02u.bfe09a19b4a6d7c44761ecf49d54b3ad
我想从 auth_user_id
的值中找到 auth_user_idq\x05\x8a\x01\x02u
。
6 个回答
如果你想了解更多关于这个内容,以及编码和解码是怎么工作的,这里有一些相关的代码。
顺便说一下,我使用的Django版本是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'))
我们来重点看看SessionBase的编码功能。
- 把会话字典转换成json格式
- 创建一个哈希盐
- 把盐加到序列化的会话上,然后进行base64编码
所以,解码就是这个过程的反向操作。
我们可以在下面的代码中简化解码功能。
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'))
这就是session.get_decoded()所做的事情。
注意:格式自原始回答以来有所更改,1.4及以上版本请查看下面的更新
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}
[更新]
我的 base64.decode 需要文件名参数,所以我尝试了 base64.b64decode,但这返回了“IndexError: list assignment index out of range”。
我其实不知道为什么我用了 base64 模块,可能是因为问题中提到了它。
你可以直接使用 str.decode
方法:
>>> pickle.loads(session_data.decode('base64'))
{'_auth_user_id': 2L, '_auth_user_backend': 'django.contrib.auth.backends.ModelBackend',
'_session_expiry': 0}
我找到了一种解决方法(见下面的回答),但我很好奇为什么这个方法不行。
从用户来源(比如 cookies)加载被 pickle 化的数据存在安全风险,所以自从这个问题被回答以来,session_data 的格式发生了变化(我应该去 Django 的 bug 跟踪器找具体问题并链接到这里,但我的番茄钟时间已经到了)。
现在的格式(自 Django 1.4 起)是“hash:json-object”,其中前面的 40 字节是加密签名,后面的部分是 JSON 数据。现在你可以忽略这个哈希值(它的作用是检查数据是否被某些 cookie 黑客篡改过)。
>>> json.loads(session_data.decode('base64')[41:])
{u'_auth_user_backend': u'django.contrib.auth.backends.ModelBackend',
u'_auth_user_id': 1}
我在使用Paulo的方法时遇到了问题(可以看看我对他回答的评论),所以我最后选择了这个方法,来自一个scottbarnham.com的博客文章:
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