]> git.ipfire.org Git - thirdparty/tornado.git/commitdiff
Add the option to request an older xsrf cookie version.
authorBen Darnell <ben@bendarnell.com>
Tue, 27 May 2014 01:18:36 +0000 (21:18 -0400)
committerBen Darnell <ben@bendarnell.com>
Tue, 27 May 2014 01:25:57 +0000 (21:25 -0400)
Fix an issue with v1 cookies on py32.

tornado/test/web_test.py
tornado/web.py

index 2c85bfb01fc9413370d0a5478d469a95e79cef02..c475520b2558fdfc727cfc3c643dac54556df93a 100644 (file)
@@ -1910,6 +1910,10 @@ class SignedValueTest(unittest.TestCase):
 class XSRFTest(SimpleHandlerTestCase):
     class Handler(RequestHandler):
         def get(self):
+            version = int(self.get_argument("version", "2"))
+            # This would be a bad idea in a real app, but in this test
+            # it's fine.
+            self.settings["xsrf_cookie_version"] = version
             self.write(self.xsrf_token)
 
         def post(self):
@@ -1922,12 +1926,14 @@ class XSRFTest(SimpleHandlerTestCase):
         super(XSRFTest, self).setUp()
         self.xsrf_token = self.get_token()
 
-    def get_token(self, old_token=None):
+    def get_token(self, old_token=None, version=None):
         if old_token is not None:
             headers = self.cookie_headers(old_token)
         else:
             headers = None
-        response = self.fetch("/", headers=headers)
+        response = self.fetch(
+            "/" if version is None else ("/?version=%d" % version),
+            headers=headers)
         response.rethrow()
         return native_str(response.body)
 
@@ -2019,3 +2025,28 @@ class XSRFTest(SimpleHandlerTestCase):
                 headers=self.cookie_headers(token))
             self.assertEqual(response.code, 200)
         self.assertEqual(len(tokens_seen), 6)
+
+    def test_versioning(self):
+        # Version 1 still produces distinct tokens per request.
+        self.assertNotEqual(self.get_token(version=1),
+                            self.get_token(version=1))
+
+        # Refreshed v1 tokens are all identical.
+        v1_token = self.get_token(version=1)
+        for i in range(5):
+            self.assertEqual(self.get_token(v1_token, version=1), v1_token)
+
+        # Upgrade to a v2 version of the same token
+        v2_token = self.get_token(v1_token)
+        self.assertNotEqual(v1_token, v2_token)
+        # Each v1 token can map to many v2 tokens.
+        self.assertNotEqual(v2_token, self.get_token(v1_token))
+
+        # The tokens are cross-compatible.
+        for cookie_token, body_token in ((v1_token, v2_token),
+                                         (v2_token, v1_token)):
+            response = self.fetch(
+                "/", method="POST",
+                body=urllib_parse.urlencode(dict(_xsrf=body_token)),
+                headers=self.cookie_headers(cookie_token))
+            self.assertEqual(response.code, 200)
index ed8148e16d7401104a318eeee5da748a9bb1890d..d8430deeb4ef397bc4a7d66a7201b9b24c8b1cff 100644 (file)
@@ -1071,13 +1071,20 @@ class RequestHandler(object):
         """
         if not hasattr(self, "_xsrf_token"):
             version, token, timestamp = self._get_raw_xsrf_token()
-            mask = os.urandom(4)
-            self._xsrf_token = b"|".join([
-                b"2",
-                binascii.b2a_hex(mask),
-                binascii.b2a_hex(_websocket_mask(mask, token)),
-                utf8(str(int(timestamp)))])
-            if version is None or version != 2:
+            output_version = self.settings.get("xsrf_cookie_version", 2)
+            if output_version == 1:
+                self._xsrf_token = binascii.b2a_hex(token)
+            elif output_version == 2:
+                mask = os.urandom(4)
+                self._xsrf_token = b"|".join([
+                    b"2",
+                    binascii.b2a_hex(mask),
+                    binascii.b2a_hex(_websocket_mask(mask, token)),
+                    utf8(str(int(timestamp)))])
+            else:
+                raise ValueError("unknown xsrf cookie version %d",
+                                 output_version)
+            if version is None:
                 expires_days = 30 if self.current_user else None
                 self.set_cookie("_xsrf", self._xsrf_token,
                                 expires_days=expires_days)
@@ -1113,7 +1120,7 @@ class RequestHandler(object):
                 return None, None, None
         elif len(cookie) == 32:
             version = 1
-            token = binascii.a2b_hex(cookie)
+            token = binascii.a2b_hex(utf8(cookie))
             # We don't have a usable timestamp in older versions.
             timestamp = int(time.time())
             return (version, token, timestamp)