diff --git a/tornado/test/web_test.py b/tornado/test/web_test.py index e004cbb68..19adce2fc 100644 --- a/tornado/test/web_test.py +++ b/tornado/test/web_test.py @@ -2866,6 +2866,35 @@ def validate(sig): ) ) + def test_malformed_timestamp(self): + # A structurally valid v2 value carrying a correct signature but a + # non-decimal timestamp field must be rejected with None rather than + # raising ValueError from int(timestamp_bytes) after the signature has + # already verified. + from tornado.web import _create_signature_v2 + from tornado.escape import utf8 + + def field(value): + return str(len(value)).encode("ascii") + b":" + value + + name = "key" + prefix = b"|".join( + [ + b"2", + field(b"0"), + field(b"a"), # non-decimal timestamp + field(utf8(name)), + field(b""), + b"", + ] + ) + cookie = prefix + _create_signature_v2(SignedValueTest.SECRET, prefix) + self.assertIsNone( + decode_signed_value( + SignedValueTest.SECRET, name, cookie, clock=self.present + ) + ) + def test_non_ascii(self): value = b"\xe9" signed = create_signed_value( diff --git a/tornado/web.py b/tornado/web.py index ad32165b1..fc6b11034 100644 --- a/tornado/web.py +++ b/tornado/web.py @@ -3767,7 +3767,13 @@ 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: + # A malformed (non-decimal) timestamp field means an invalid signed + # value; reject it with None rather than raising, consistent with the + # rest of this function and get_signed_cookie's documented behavior. + return None if timestamp < clock() - max_age_days * 86400: # The signature has expired. return None