这是一个在App Engine上处理会话的基本类:
"""Lightweight implementation of cookie-based sessions for Google App Engine.
Classes:
Session
"""
import os
import random
import Cookie
from google.appengine.api import memcache
_COOKIE_NAME = 'app-sid'
_COOKIE_PATH = '/'
_SESSION_EXPIRE_TIME = 180 * 60
class Session(object):
"""Cookie-based session implementation using Memcached."""
def __init__(self):
self.sid = None
self.key = None
self.session = None
cookie_str = os.environ.get('HTTP_COOKIE', '')
self.cookie = Cookie.SimpleCookie()
self.cookie.load(cookie_str)
if self.cookie.get(_COOKIE_NAME):
self.sid = self.cookie[_COOKIE_NAME].value
self.key = 'session-' + self.sid
self.session = memcache.get(self.key)
if self.session:
self._update_memcache()
else:
self.sid = str(random.random())[5:] + str(random.random())[5:]
self.key = 'session-' + self.sid
self.session = dict()
memcache.add(self.key, self.session, _SESSION_EXPIRE_TIME)
self.cookie[_COOKIE_NAME] = self.sid
self.cookie[_COOKIE_NAME]['path'] = _COOKIE_PATH
print self.cookie
def __len__(self):
return len(self.session)
def __getitem__(self, key):
if key in self.session:
return self.session[key]
raise KeyError(str(key))
def __setitem__(self, key, value):
self.session[key] = value
self._update_memcache()
def __delitem__(self, key):
if key in self.session:
del self.session[key]
self._update_memcache()
return None
raise KeyError(str(key))
def __contains__(self, item):
try:
i = self.__getitem__(item)
except KeyError:
return False
return True
def _update_memcache(self):
memcache.replace(self.key, self.session, _SESSION_EXPIRE_TIME)
我想就如何改进代码以提高安全性提出一些建议。
注意:在生产版本中,它还会在数据存储区中保存会话的副本。
注意':我知道有更多完整的在线实施,但我想了解更多有关此主题的信息,所以请不要回答“使用”或“使用”其他“图书馆。
答案 0 :(得分:5)
以下是简化实施的建议。
您正在创建一个随机临时密钥,您将其用作内存缓存中的会话密钥。您注意到您也将会话存储在数据存储区中(它将有另一个密钥)。
为什么不随机化会话的数据存储区密钥,然后将其用作数据库和内存缓存的唯一密钥(如果需要)?这种简化是否会引入任何新的安全问题?
以下是为Session模型创建随机数据存储区密钥的一些代码:
# Get a random integer to use as the session's datastore ID.
# (So it can be stored in a cookie without being 'guessable'.)
random.seed();
id = None;
while None==id or Session.get_by_id( id ):
id = random.randrange( sys.maxint );
seshKey = db.Key.from_path( 'Session', id );
session = Session( key = seshKey );
要从会话中获取ID(即存储在cookie中),请使用:
sid = session.key().id();
在从cookie中读取'sid'后检索会话实例:
session = Session.get_by_id( sid );
答案 1 :(得分:0)
您可以添加一些额外的安全措施。
首先,我认为使用存储在会话实例中的信息来验证每个新请求是很常见的。例如,您可以验证在会话期间IP地址和用户代理是否更改:
newip = str( request.remote_addr );
if sesh.ip_addr != newip:
logging.warn( "Session IP has changed to %s." % newip);
newua = rh.request.headers.get( 'User-Agent', None );
if sesh.agent != newua:
logging.warn( "Session UA has changed to %s." % newua );
另外,也许最好不要无限期地更新会话?我认为像Google这样的网站最终会要求您再次登录,如果您试图让会话持续很长时间。
我想每次会话更新时慢慢减少_SESSION_EXPIRE_TIME会很容易,但这不是一个非常好的解决方案。理想情况下,选择何时强制用户再次登录会考虑到您网站的流量和安全要求。