[Python-checkins] bpo-34911: Added support for secure websocket cookies (GH-9734)

Andrew Svetlov webhook-mailer at python.org
Mon Oct 8 14:49:36 EDT 2018


https://github.com/python/cpython/commit/4c339970570d07916bee6ade51f4e9781d51627a
commit: 4c339970570d07916bee6ade51f4e9781d51627a
branch: master
author: Paul Bailey <paul.m.bailey at gmail.com>
committer: Andrew Svetlov <andrew.svetlov at gmail.com>
date: 2018-10-08T21:49:29+03:00
summary:

bpo-34911: Added support for secure websocket cookies (GH-9734)

files:
A Misc/NEWS.d/next/Library/2018-10-08-15-22-02.bpo-34911.hCy0Fv.rst
M Doc/library/http.cookiejar.rst
M Lib/http/cookiejar.py
M Lib/test/test_http_cookiejar.py

diff --git a/Doc/library/http.cookiejar.rst b/Doc/library/http.cookiejar.rst
index d8da6683a3a5..4c8be2917b22 100644
--- a/Doc/library/http.cookiejar.rst
+++ b/Doc/library/http.cookiejar.rst
@@ -78,14 +78,16 @@ The following classes are provided:
    from / returned to the server.
 
 
-.. class:: DefaultCookiePolicy( blocked_domains=None, allowed_domains=None, netscape=True, rfc2965=False, rfc2109_as_netscape=None, hide_cookie2=False, strict_domain=False, strict_rfc2965_unverifiable=True, strict_ns_unverifiable=False, strict_ns_domain=DefaultCookiePolicy.DomainLiberal, strict_ns_set_initial_dollar=False, strict_ns_set_path=False )
+.. class:: DefaultCookiePolicy( blocked_domains=None, allowed_domains=None, netscape=True, rfc2965=False, rfc2109_as_netscape=None, hide_cookie2=False, strict_domain=False, strict_rfc2965_unverifiable=True, strict_ns_unverifiable=False, strict_ns_domain=DefaultCookiePolicy.DomainLiberal, strict_ns_set_initial_dollar=False, strict_ns_set_path=False, secure_protocols=("https", "wss") )
 
    Constructor arguments should be passed as keyword arguments only.
    *blocked_domains* is a sequence of domain names that we never accept cookies
    from, nor return cookies to. *allowed_domains* if not :const:`None`, this is a
-   sequence of the only domains for which we accept and return cookies.  For all
-   other arguments, see the documentation for :class:`CookiePolicy` and
-   :class:`DefaultCookiePolicy` objects.
+   sequence of the only domains for which we accept and return cookies.
+   *secure_protocols* is a sequence of protocols for which secure cookies can be
+   added to. By default *https* and *wss* (secure websocket) are considered
+   secure protocols. For all other arguments, see the documentation for
+   :class:`CookiePolicy` and :class:`DefaultCookiePolicy` objects.
 
    :class:`DefaultCookiePolicy` implements the standard accept / reject rules for
    Netscape and :rfc:`2965` cookies.  By default, :rfc:`2109` cookies (ie. cookies
diff --git a/Lib/http/cookiejar.py b/Lib/http/cookiejar.py
index e0f1032b2816..0ba8200f325a 100644
--- a/Lib/http/cookiejar.py
+++ b/Lib/http/cookiejar.py
@@ -878,6 +878,7 @@ def __init__(self,
                  strict_ns_domain=DomainLiberal,
                  strict_ns_set_initial_dollar=False,
                  strict_ns_set_path=False,
+                 secure_protocols=("https", "wss")
                  ):
         """Constructor arguments should be passed as keyword arguments only."""
         self.netscape = netscape
@@ -890,6 +891,7 @@ def __init__(self,
         self.strict_ns_domain = strict_ns_domain
         self.strict_ns_set_initial_dollar = strict_ns_set_initial_dollar
         self.strict_ns_set_path = strict_ns_set_path
+        self.secure_protocols = secure_protocols
 
         if blocked_domains is not None:
             self._blocked_domains = tuple(blocked_domains)
@@ -1116,7 +1118,7 @@ def return_ok_verifiability(self, cookie, request):
         return True
 
     def return_ok_secure(self, cookie, request):
-        if cookie.secure and request.type != "https":
+        if cookie.secure and request.type not in self.secure_protocols:
             _debug("   secure cookie with non-secure request")
             return False
         return True
diff --git a/Lib/test/test_http_cookiejar.py b/Lib/test/test_http_cookiejar.py
index 6fee4df10a40..968725901f22 100644
--- a/Lib/test/test_http_cookiejar.py
+++ b/Lib/test/test_http_cookiejar.py
@@ -984,6 +984,61 @@ def test_secure(self):
                     c._cookies["www.acme.com"]["/"]["foo2"].secure,
                     "secure cookie registered non-secure")
 
+    def test_secure_block(self):
+        pol = DefaultCookiePolicy()
+        c = CookieJar(policy=pol)
+
+        headers = ["Set-Cookie: session=narf; secure; path=/"]
+        req = urllib.request.Request("https://www.acme.com/")
+        res = FakeResponse(headers, "https://www.acme.com/")
+        c.extract_cookies(res, req)
+        self.assertEqual(len(c), 1)
+
+        req = urllib.request.Request("https://www.acme.com/")
+        c.add_cookie_header(req)
+        self.assertTrue(req.has_header("Cookie"))
+
+        req = urllib.request.Request("http://www.acme.com/")
+        c.add_cookie_header(req)
+        self.assertFalse(req.has_header("Cookie"))
+
+        # secure websocket protocol
+        req = urllib.request.Request("wss://www.acme.com/")
+        c.add_cookie_header(req)
+        self.assertTrue(req.has_header("Cookie"))
+
+        # non-secure websocket protocol
+        req = urllib.request.Request("ws://www.acme.com/")
+        c.add_cookie_header(req)
+        self.assertFalse(req.has_header("Cookie"))
+
+    def test_custom_secure_protocols(self):
+        pol = DefaultCookiePolicy(secure_protocols=["foos"])
+        c = CookieJar(policy=pol)
+
+        headers = ["Set-Cookie: session=narf; secure; path=/"]
+        req = urllib.request.Request("https://www.acme.com/")
+        res = FakeResponse(headers, "https://www.acme.com/")
+        c.extract_cookies(res, req)
+        self.assertEqual(len(c), 1)
+
+        # test https removed from secure protocol list
+        req = urllib.request.Request("https://www.acme.com/")
+        c.add_cookie_header(req)
+        self.assertFalse(req.has_header("Cookie"))
+
+        req = urllib.request.Request("http://www.acme.com/")
+        c.add_cookie_header(req)
+        self.assertFalse(req.has_header("Cookie"))
+
+        req = urllib.request.Request("foos://www.acme.com/")
+        c.add_cookie_header(req)
+        self.assertTrue(req.has_header("Cookie"))
+
+        req = urllib.request.Request("foo://www.acme.com/")
+        c.add_cookie_header(req)
+        self.assertFalse(req.has_header("Cookie"))
+
     def test_quote_cookie_value(self):
         c = CookieJar(policy=DefaultCookiePolicy(rfc2965=True))
         interact_2965(c, "http://www.acme.com/", r'foo=\b"a"r; Version=1')
diff --git a/Misc/NEWS.d/next/Library/2018-10-08-15-22-02.bpo-34911.hCy0Fv.rst b/Misc/NEWS.d/next/Library/2018-10-08-15-22-02.bpo-34911.hCy0Fv.rst
new file mode 100644
index 000000000000..d3509475306b
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2018-10-08-15-22-02.bpo-34911.hCy0Fv.rst
@@ -0,0 +1,3 @@
+Added *secure_protocols* argument to *http.cookiejar.DefaultCookiePolicy* to
+allow for tweaking of protocols and also to add support by default for
+*wss*, the secure websocket protocol.



More information about the Python-checkins mailing list