Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions tornado/test/web_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -2904,6 +2904,49 @@ def test_key_version_retrieval(self):
key_version = get_signature_key_version(signed)
self.assertEqual(1, key_version)

def test_malformed_timestamp_v1(self):
# Construct a v1 signed value with a non-decimal timestamp.
# The signature is computed over the malformed timestamp, so it
# passes HMAC verification but int() conversion should fail
# gracefully (return None) instead of raising ValueError.
secret = b"s"
name = "a"
# Build: base64(value) | timestamp | signature
value_b64 = b"dg==" # base64(b"v")
timestamp = b"not-a-number"
sig = _create_signature_v1(secret, name, value_b64, timestamp)
cookie = b"|".join([value_b64, timestamp, sig])
self.assertIsNone(
decode_signed_value(secret, name, cookie, clock=lambda: 1_500_000_000)
)

def test_malformed_timestamp_v2(self):
# Construct a v2 signed value with a non-decimal timestamp field.
# Same principle: valid HMAC but non-numeric timestamp must not
# raise ValueError.
from tornado.web import _create_signature_v2

secret = b"s"
name = "a"

def field(value: bytes) -> bytes:
return str(len(value)).encode("ascii") + b":" + value

prefix = b"|".join(
[
b"2",
field(b"0"), # key_version
field(b"a"), # non-decimal timestamp
field(utf8(name)),
field(b""), # empty value
b"",
]
)
cookie = prefix + _create_signature_v2(secret, prefix)
self.assertIsNone(
decode_signed_value(secret, name, cookie, clock=lambda: 1_500_000_000)
)


class XSRFTest(SimpleHandlerTestCase):
class Handler(RequestHandler):
Expand Down
10 changes: 8 additions & 2 deletions tornado/web.py
Original file line number Diff line number Diff line change
Expand Up @@ -3692,7 +3692,10 @@ def _decode_signed_value_v1(
if not hmac.compare_digest(parts[2], signature):
gen_log.warning("Invalid cookie signature %r", value)
return None
timestamp = int(parts[1])
try:
timestamp = int(parts[1])
except ValueError:
return None
if timestamp < clock() - max_age_days * 86400:
gen_log.warning("Expired cookie %r", value)
return None
Expand Down Expand Up @@ -3763,7 +3766,10 @@ def _decode_signed_value_v2(
return None
if name_field != utf8(name):
return None
timestamp = int(timestamp_bytes)
try:
timestamp = int(timestamp_bytes)
except ValueError:
return None
if timestamp < clock() - max_age_days * 86400:
# The signature has expired.
return None
Expand Down