Commit 4c339970 authored by Paul Bailey's avatar Paul Bailey Committed by Andrew Svetlov

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

parent af5658ae
...@@ -78,14 +78,16 @@ The following classes are provided: ...@@ -78,14 +78,16 @@ The following classes are provided:
from / returned to the server. 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. Constructor arguments should be passed as keyword arguments only.
*blocked_domains* is a sequence of domain names that we never accept cookies *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 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 sequence of the only domains for which we accept and return cookies.
other arguments, see the documentation for :class:`CookiePolicy` and *secure_protocols* is a sequence of protocols for which secure cookies can be
:class:`DefaultCookiePolicy` objects. 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 :class:`DefaultCookiePolicy` implements the standard accept / reject rules for
Netscape and :rfc:`2965` cookies. By default, :rfc:`2109` cookies (ie. cookies Netscape and :rfc:`2965` cookies. By default, :rfc:`2109` cookies (ie. cookies
......
...@@ -878,6 +878,7 @@ class DefaultCookiePolicy(CookiePolicy): ...@@ -878,6 +878,7 @@ class DefaultCookiePolicy(CookiePolicy):
strict_ns_domain=DomainLiberal, strict_ns_domain=DomainLiberal,
strict_ns_set_initial_dollar=False, strict_ns_set_initial_dollar=False,
strict_ns_set_path=False, strict_ns_set_path=False,
secure_protocols=("https", "wss")
): ):
"""Constructor arguments should be passed as keyword arguments only.""" """Constructor arguments should be passed as keyword arguments only."""
self.netscape = netscape self.netscape = netscape
...@@ -890,6 +891,7 @@ class DefaultCookiePolicy(CookiePolicy): ...@@ -890,6 +891,7 @@ class DefaultCookiePolicy(CookiePolicy):
self.strict_ns_domain = strict_ns_domain self.strict_ns_domain = strict_ns_domain
self.strict_ns_set_initial_dollar = strict_ns_set_initial_dollar self.strict_ns_set_initial_dollar = strict_ns_set_initial_dollar
self.strict_ns_set_path = strict_ns_set_path self.strict_ns_set_path = strict_ns_set_path
self.secure_protocols = secure_protocols
if blocked_domains is not None: if blocked_domains is not None:
self._blocked_domains = tuple(blocked_domains) self._blocked_domains = tuple(blocked_domains)
...@@ -1116,7 +1118,7 @@ class DefaultCookiePolicy(CookiePolicy): ...@@ -1116,7 +1118,7 @@ class DefaultCookiePolicy(CookiePolicy):
return True return True
def return_ok_secure(self, cookie, request): 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") _debug(" secure cookie with non-secure request")
return False return False
return True return True
......
...@@ -984,6 +984,61 @@ class CookieTests(unittest.TestCase): ...@@ -984,6 +984,61 @@ class CookieTests(unittest.TestCase):
c._cookies["www.acme.com"]["/"]["foo2"].secure, c._cookies["www.acme.com"]["/"]["foo2"].secure,
"secure cookie registered non-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): def test_quote_cookie_value(self):
c = CookieJar(policy=DefaultCookiePolicy(rfc2965=True)) c = CookieJar(policy=DefaultCookiePolicy(rfc2965=True))
interact_2965(c, "http://www.acme.com/", r'foo=\b"a"r; Version=1') interact_2965(c, "http://www.acme.com/", r'foo=\b"a"r; Version=1')
......
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.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment