google-app-engine - Google App Engine - 安全 Cookie

标签 google-app-engine session forms-authentication cookies tornado

我一直在寻找一种在 Google App Engine 中进行基于 cookie 的身份验证/ session 的方法,因为我不喜欢基于内存缓存的 session 的想法,而且我也不喜欢强制用户创建 google 的想法帐户只是为了使用一个网站。我偶然发现某人的 posting 提到了 Tornado 框架中的一些签名 cookie 函数,它看起来像我需要的。我的想法是将用户的 id 存储在防篡改 cookie 中,并且可能使用请求处理程序的装饰器来测试用户的身份验证状态,作为附带的好处,用户 id 将可用于请求处理程序数据存储工作等。这个概念类似于 ASP.NET 中的表单例份验证。这段代码来自Tornado框架的web.py模块。

根据文档字符串,它“对 cookie 进行签名和时间戳,使其无法被伪造”并且 “如果验证通过,则返回给定的签名 cookie,否则返回 None。”

我曾尝试在 App Engine 项目中使用它,但我不明白尝试让这些方法在请求处理程序的上下文中工作的细微差别。有人可以告诉我正确的方法来做到这一点,而不会失去 FriendFeed 开发人员赋予它的功能吗? set_secure_cookie 和 get_secure_cookie 部分是最重要的,但如果能够同时使用其他方法就好了。

#!/usr/bin/env python

import Cookie
import base64
import time
import hashlib
import hmac
import datetime
import re
import calendar
import email.utils
import logging

def _utf8(s):
    if isinstance(s, unicode):
        return s.encode("utf-8")
    assert isinstance(s, str)
    return s

def _unicode(s):
    if isinstance(s, str):
        try:
            return s.decode("utf-8")
        except UnicodeDecodeError:
            raise HTTPError(400, "Non-utf8 argument")
    assert isinstance(s, unicode)
    return s 

def _time_independent_equals(a, b):
    if len(a) != len(b):
        return False
    result = 0
    for x, y in zip(a, b):
        result |= ord(x) ^ ord(y)
    return result == 0

def cookies(self):
    """A dictionary of Cookie.Morsel objects."""
    if not hasattr(self,"_cookies"):
        self._cookies = Cookie.BaseCookie()
        if "Cookie" in self.request.headers:
            try:
                self._cookies.load(self.request.headers["Cookie"])
            except:
                self.clear_all_cookies()
    return self._cookies

def _cookie_signature(self,*parts):
    self.require_setting("cookie_secret","secure cookies")
    hash = hmac.new(self.application.settings["cookie_secret"],
                    digestmod=hashlib.sha1)
    for part in parts:hash.update(part)
    return hash.hexdigest()

def get_cookie(self,name,default=None):
    """Gets the value of the cookie with the given name,else default."""
    if name in self.cookies:
        return self.cookies[name].value
    return default

def set_cookie(self,name,value,domain=None,expires=None,path="/",
               expires_days=None):
    """Sets the given cookie name/value with the given options."""
    name = _utf8(name)
    value = _utf8(value)
    if re.search(r"[\x00-\x20]",name + value):
        # Don't let us accidentally inject bad stuff
        raise ValueError("Invalid cookie %r:%r" % (name,value))
    if not hasattr(self,"_new_cookies"):
        self._new_cookies = []
    new_cookie = Cookie.BaseCookie()
    self._new_cookies.append(new_cookie)
    new_cookie[name] = value
    if domain:
        new_cookie[name]["domain"] = domain
    if expires_days is not None and not expires:
        expires = datetime.datetime.utcnow() + datetime.timedelta(
            days=expires_days)
    if expires:
        timestamp = calendar.timegm(expires.utctimetuple())
        new_cookie[name]["expires"] = email.utils.formatdate(
            timestamp,localtime=False,usegmt=True)
    if path:
        new_cookie[name]["path"] = path

def clear_cookie(self,name,path="/",domain=None):
    """Deletes the cookie with the given name."""
    expires = datetime.datetime.utcnow() - datetime.timedelta(days=365)
    self.set_cookie(name,value="",path=path,expires=expires,
                    domain=domain)

def clear_all_cookies(self):
    """Deletes all the cookies the user sent with this request."""
    for name in self.cookies.iterkeys():
        self.clear_cookie(name)

def set_secure_cookie(self,name,value,expires_days=30,**kwargs):
    """Signs and timestamps a cookie so it cannot be forged"""
    timestamp = str(int(time.time()))
    value = base64.b64encode(value)
    signature = self._cookie_signature(name,value,timestamp)
    value = "|".join([value,timestamp,signature])
    self.set_cookie(name,value,expires_days=expires_days,**kwargs)

def get_secure_cookie(self,name,include_name=True,value=None):
    """Returns the given signed cookie if it validates,or None"""
    if value is None:value = self.get_cookie(name)
    if not value:return None
    parts = value.split("|")
    if len(parts) != 3:return None
    if include_name:
        signature = self._cookie_signature(name,parts[0],parts[1])
    else:
        signature = self._cookie_signature(parts[0],parts[1])
    if not _time_independent_equals(parts[2],signature):
        logging.warning("Invalid cookie signature %r",value)
        return None
    timestamp = int(parts[1])
    if timestamp < time.time() - 31 * 86400:
        logging.warning("Expired cookie %r",value)
        return None
    try:
        return base64.b64decode(parts[0])
    except:
        return None

uid=1234|1234567890|d32b9e9c67274fa062e2599fd659cc14

零件:
1. uid是key的名字
2. 1234是你的clear值
3. 1234567890为时间戳
4. d32b9e9c67274fa062e2599fd659cc14 是由值和时间戳组成的签名

最佳答案

Tornado 从未打算与 App Engine 一起工作(它始终是“自己的服务器”)。您为什么不选择一些从单词“go”开始用于 App Engine 的框架,它是轻量级和花花公子的,例如 tipfy ?它使用自己的用户系统或任何 App Engine 自己的 users、OpenIn、OAuth 和 Facebook 为您提供身份验证;与安全 cookie 或 GAE 数据存储的 session ;除此之外,所有这些都采用基于 WSGI 和 Werkzeug 的超轻量级“非框架”方法。有什么不喜欢的?!

关于google-app-engine - Google App Engine - 安全 Cookie,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2532004/

相关文章:

python - 为子域配置 GAE 应用程序 app.yaml

google-app-engine - AppEngine,从控制面板清除内存缓存

python-2.7 - 在 python 中从大型 BigQuery 表中获取数据

google-app-engine - 是否可以在 App Engine 标准环境中使用 Google Cloud CDN?

asp.net - 表单例份验证 : Is it secure?

asp.net - ASP.NET 中的 session 和身份验证

java - 无法序列化 session 的 session 属性 SPRING_SECURITY_CONTEXT

php - MySQL 值作为 PHP session

java - 安卓 session 管理

asp.net - 为什么我的 ASP.NET MVC 持久身份验证 cookie 不起作用?