From 22e8614fdd045a1d2ac264c9917814ae4852170b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Tis=C3=A4ter?= Date: Mon, 7 Jul 2014 23:29:44 +0200 Subject: [PATCH 1/4] Remove length requirement from v1 XSRF tokens --- tornado/test/web_test.py | 14 ++++++++++++++ tornado/web.py | 9 +++++---- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/tornado/test/web_test.py b/tornado/test/web_test.py index c475520b..2f595af6 100644 --- a/tornado/test/web_test.py +++ b/tornado/test/web_test.py @@ -1954,6 +1954,20 @@ class XSRFTest(SimpleHandlerTestCase): body=urllib_parse.urlencode(dict(_xsrf=self.xsrf_token))) self.assertEqual(response.code, 403) + def test_xsrf_success_short_token(self): + with ExpectLog(gen_log, ".*XSRF cookie does not match POST"): + response = self.fetch( + "/", method="POST", + body=urllib_parse.urlencode(dict(_xsrf='deadbeef'))) + self.assertEqual(response.code, 403) + + def test_xsrf_success_non_hex_token(self): + with ExpectLog(gen_log, ".*XSRF cookie is not a hexadecimal"): + response = self.fetch( + "/", method="POST", + body=urllib_parse.urlencode(dict(_xsrf='xoxo'))) + self.assertEqual(response.code, 400) + def test_xsrf_fail_cookie_no_body(self): with ExpectLog(gen_log, ".*'_xsrf' argument missing"): response = self.fetch( diff --git a/tornado/web.py b/tornado/web.py index 9fe2f77b..7147c17e 100644 --- a/tornado/web.py +++ b/tornado/web.py @@ -1140,14 +1140,15 @@ class RequestHandler(object): else: # Treat unknown versions as not present instead of failing. return None, None, None - elif len(cookie) == 32: + else: version = 1 - token = binascii.a2b_hex(utf8(cookie)) + try: + token = binascii.a2b_hex(utf8(cookie)) + except TypeError: + raise HTTPError(400, "XSRF cookie is not a hexadecimal") # We don't have a usable timestamp in older versions. timestamp = int(time.time()) return (version, token, timestamp) - else: - return None, None, None def check_xsrf_cookie(self): """Verifies that the ``_xsrf`` cookie matches the ``_xsrf`` argument. From d881dd2f87813efa62af7df6755fb83b55f7a698 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Tis=C3=A4ter?= Date: Tue, 8 Jul 2014 09:07:18 +0200 Subject: [PATCH 2/4] Turn short and hex XSRF tests into success tests --- tornado/test/web_test.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/tornado/test/web_test.py b/tornado/test/web_test.py index 2f595af6..0ac3b104 100644 --- a/tornado/test/web_test.py +++ b/tornado/test/web_test.py @@ -1954,20 +1954,6 @@ class XSRFTest(SimpleHandlerTestCase): body=urllib_parse.urlencode(dict(_xsrf=self.xsrf_token))) self.assertEqual(response.code, 403) - def test_xsrf_success_short_token(self): - with ExpectLog(gen_log, ".*XSRF cookie does not match POST"): - response = self.fetch( - "/", method="POST", - body=urllib_parse.urlencode(dict(_xsrf='deadbeef'))) - self.assertEqual(response.code, 403) - - def test_xsrf_success_non_hex_token(self): - with ExpectLog(gen_log, ".*XSRF cookie is not a hexadecimal"): - response = self.fetch( - "/", method="POST", - body=urllib_parse.urlencode(dict(_xsrf='xoxo'))) - self.assertEqual(response.code, 400) - def test_xsrf_fail_cookie_no_body(self): with ExpectLog(gen_log, ".*'_xsrf' argument missing"): response = self.fetch( @@ -1975,6 +1961,20 @@ class XSRFTest(SimpleHandlerTestCase): headers=self.cookie_headers()) self.assertEqual(response.code, 403) + def test_xsrf_success_short_token(self): + response = self.fetch( + "/", method="POST", + body=urllib_parse.urlencode(dict(_xsrf='deadbeef')), + headers=self.cookie_headers(token='deadbeef')) + self.assertEqual(response.code, 200) + + def test_xsrf_success_non_hex_token(self): + response = self.fetch( + "/", method="POST", + body=urllib_parse.urlencode(dict(_xsrf='xoxo')), + headers=self.cookie_headers(token='xoxo')) + self.assertEqual(response.code, 200) + def test_xsrf_success_post_body(self): response = self.fetch( "/", method="POST", From 2299cf96bb12ff1ffc26f5e6bd071fdd37fd2204 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Tis=C3=A4ter?= Date: Tue, 8 Jul 2014 09:07:48 +0200 Subject: [PATCH 3/4] Don't raise when failing to hex decode XSRF v1 --- tornado/web.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tornado/web.py b/tornado/web.py index 7147c17e..11dab74f 100644 --- a/tornado/web.py +++ b/tornado/web.py @@ -1145,7 +1145,7 @@ class RequestHandler(object): try: token = binascii.a2b_hex(utf8(cookie)) except TypeError: - raise HTTPError(400, "XSRF cookie is not a hexadecimal") + token = utf8(cookie) # We don't have a usable timestamp in older versions. timestamp = int(time.time()) return (version, token, timestamp) From ab688e7a1befb4a0d1fbb3c9b49cbb19c8d2276c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Tis=C3=A4ter?= Date: Tue, 8 Jul 2014 15:52:25 +0200 Subject: [PATCH 4/4] Catch `binascii.a2b_hex` errors on Python 3 --- tornado/web.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tornado/web.py b/tornado/web.py index 11dab74f..a0285a4c 100644 --- a/tornado/web.py +++ b/tornado/web.py @@ -1144,7 +1144,7 @@ class RequestHandler(object): version = 1 try: token = binascii.a2b_hex(utf8(cookie)) - except TypeError: + except (binascii.Error, TypeError): token = utf8(cookie) # We don't have a usable timestamp in older versions. timestamp = int(time.time())