diff --git a/bottle.py b/bottle.py index 4b816f6ee..f0eb9f34a 100755 --- a/bottle.py +++ b/bottle.py @@ -128,7 +128,7 @@ def getargspec(func): from urllib.parse import urljoin, SplitResult as UrlSplitResult from urllib.parse import urlencode, quote as urlquote, unquote as urlunquote urlunquote = functools.partial(urlunquote, encoding='latin1') - from http.cookies import SimpleCookie + from http.cookies import SimpleCookie, Morsel, CookieError from collections import MutableMapping as DictMixin import pickle from io import BytesIO @@ -1813,6 +1813,10 @@ def set_cookie(self, name, value, secret=None, digestmod=hashlib.sha256, **optio :param secure: limit the cookie to HTTPS connections (default: off). :param httponly: prevents client-side javascript to read this cookie (default: off, requires Python 2.6 or newer). + :param same_site: disables third-party use for a cookie. + Allowed attributes: `lax` and `strict`. + In strict mode the cookie will never be sent. + In lax mode the cookie is only sent with a top-level GET request. If neither `expires` nor `max_age` is set (default), the cookie will expire at the end of the browser session (as soon as the browser @@ -1834,7 +1838,10 @@ def set_cookie(self, name, value, secret=None, digestmod=hashlib.sha256, **optio """ if not self._cookies: self._cookies = SimpleCookie() - + + # To add "SameSite" cookie support. + Morsel._reserved['same-site'] = 'SameSite' + if secret: if not isinstance(value, basestring): depr(0, 13, "Pickling of arbitrary objects into cookies is " @@ -1863,6 +1870,9 @@ def set_cookie(self, name, value, secret=None, digestmod=hashlib.sha256, **optio elif isinstance(value, (int, float)): value = time.gmtime(value) value = time.strftime("%a, %d %b %Y %H:%M:%S GMT", value) + # check values for SameSite cookie, because it's not natively supported by http.cookies. + if key == 'same_site' and value.lower() not in ('lax', 'strict'): + raise CookieError("Invalid attribute %r" % (key,)) if key in ('secure', 'httponly') and not value: continue self._cookies[name][key.replace('_', '-')] = value diff --git a/docs/tutorial.rst b/docs/tutorial.rst index 363c25556..326e05d0e 100755 --- a/docs/tutorial.rst +++ b/docs/tutorial.rst @@ -420,6 +420,7 @@ The :meth:`Response.set_cookie` method accepts a number of additional keyword ar * **path:** Limit the cookie to a given path (default: ``/``) * **secure:** Limit the cookie to HTTPS connections (default: off). * **httponly:** Prevent client-side javascript to read this cookie (default: off, requires Python 2.7 or newer). +* **same_site:** Disables third-party use for a cookie. Allowed attributes: `lax` and `strict`. In strict mode the cookie will never be sent. In lax mode the cookie is only sent with a top-level GET request. If neither `expires` nor `max_age` is set, the cookie expires at the end of the browser session or as soon as the browser window is closed. There are some other gotchas you should consider when using cookies: