from tornado.template import DictLoader
from tornado.testing import LogTrapTestCase, AsyncHTTPTestCase
from tornado.util import b, bytes_type
-from tornado.web import RequestHandler, _O, authenticated, Application, asynchronous, url, HTTPError, StaticFileHandler
+from tornado.web import RequestHandler, _O, authenticated, Application, asynchronous, url, HTTPError, StaticFileHandler, _create_signature
import binascii
import logging
assert match
timestamp = match.group(1)
sig = match.group(2)
- self.assertEqual(handler._cookie_signature('foo', '12345678',
- timestamp), sig)
+ self.assertEqual(
+ _create_signature(handler.application.settings["cookie_secret"],
+ 'foo', '12345678', timestamp),
+ sig)
# shifting digits from payload to timestamp doesn't alter signature
# (this is not desirable behavior, just confirming that that's how it
# works)
self.assertEqual(
- handler._cookie_signature('foo', '1234', b('5678') + timestamp),
+ _create_signature(handler.application.settings["cookie_secret"],
+ 'foo', '1234', b('5678') + timestamp),
sig)
# tamper with the cookie
handler._cookies['foo'] = utf8('1234|5678%s|%s' % (timestamp, sig))
method for non-cookie uses. To decode a value not stored
as a cookie use the optional value argument to get_secure_cookie.
"""
- timestamp = utf8(str(int(time.time())))
- value = base64.b64encode(utf8(value))
- signature = self._cookie_signature(name, value, timestamp)
- value = b("|").join([value, timestamp, signature])
- return value
+ self.require_setting("cookie_secret", "secure cookies")
+ return create_signed_value(self.application.settings["cookie_secret"],
+ name, value)
def get_secure_cookie(self, name, value=None, max_age_days=31):
"""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 = utf8(value).split(b("|"))
- if len(parts) != 3: return None
- signature = self._cookie_signature(name, 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() - max_age_days * 86400:
- logging.warning("Expired cookie %r", value)
- return None
- if timestamp > time.time() + 31 * 86400:
- # _cookie_signature does not hash a delimiter between the
- # parts of the cookie, so an attacker could transfer trailing
- # digits from the payload to the timestamp without altering the
- # signature. For backwards compatibility, sanity-check timestamp
- # here instead of modifying _cookie_signature.
- logging.warning("Cookie timestamp in future; possible tampering %r", value)
- return None
- if parts[1].startswith(b("0")):
- logging.warning("Tampered cookie %r", value)
- try:
- return base64.b64decode(parts[0])
- except Exception:
- return None
-
- def _cookie_signature(self, *parts):
self.require_setting("cookie_secret", "secure cookies")
- hash = hmac.new(utf8(self.application.settings["cookie_secret"]),
- digestmod=hashlib.sha1)
- for part in parts: hash.update(utf8(part))
- return utf8(hash.hexdigest())
+ if value is None: value = self.get_cookie(name)
+ return decode_signed_value(self.application.settings["cookie_secret"],
+ name, value, max_age_days=max_age_days)
def redirect(self, url, permanent=False):
"""Sends a redirect to the given (optionally relative) URL."""
result |= ord(x) ^ ord(y)
return result == 0
+def create_signed_value(secret, name, value):
+ timestamp = utf8(str(int(time.time())))
+ value = base64.b64encode(utf8(value))
+ signature = _create_signature(secret, name, value, timestamp)
+ value = b("|").join([value, timestamp, signature])
+ return value
+
+def decode_signed_value(secret, name, value, max_age_days=31):
+ if not value: return None
+ parts = utf8(value).split(b("|"))
+ if len(parts) != 3: return None
+ signature = _create_signature(secret, name, 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() - max_age_days * 86400:
+ logging.warning("Expired cookie %r", value)
+ return None
+ if timestamp > time.time() + 31 * 86400:
+ # _cookie_signature does not hash a delimiter between the
+ # parts of the cookie, so an attacker could transfer trailing
+ # digits from the payload to the timestamp without altering the
+ # signature. For backwards compatibility, sanity-check timestamp
+ # here instead of modifying _cookie_signature.
+ logging.warning("Cookie timestamp in future; possible tampering %r", value)
+ return None
+ if parts[1].startswith(b("0")):
+ logging.warning("Tampered cookie %r", value)
+ try:
+ return base64.b64decode(parts[0])
+ except Exception:
+ return None
+
+def _create_signature(secret, *parts):
+ hash = hmac.new(utf8(secret), digestmod=hashlib.sha1)
+ for part in parts: hash.update(utf8(part))
+ return utf8(hash.hexdigest())
+
class _O(dict):
"""Makes a dictionary behave like an object."""