diff --git a/docs/config.rst b/docs/config.rst index c1854c8e..2e2833f9 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -210,12 +210,14 @@ The following configuration values are used internally by Flask: .. py:data:: SESSION_COOKIE_SAMESITE - Browser will only send cookies to the domain that created them. - There are two possible values for the same-site attribute: "Strict" and "Lax" - If set to "None", the samesite flag is not set. + Restrict how cookies are sent with requests from external sites. Can + be set to ``'Lax'`` (recommended) or ``'Strict'``. + See :ref:`security-cookie`. Default: ``None`` + .. versionadded:: 1.0 + .. py:data:: PERMANENT_SESSION_LIFETIME If ``session.permanent`` is true, the cookie's expiration will be set this @@ -369,13 +371,15 @@ The following configuration values are used internally by Flask: ``LOGGER_HANDLER_POLICY``, ``EXPLAIN_TEMPLATE_LOADING`` .. versionchanged:: 1.0 - ``LOGGER_NAME`` and ``LOGGER_HANDLER_POLICY`` were removed. See :ref:`logging` for information about configuration. Added :data:`ENV` to reflect the :envvar:`FLASK_ENV` environment variable. + Added :data:`SESSION_COOKIE_SAMESITE` to control the session + cookie's ``SameSite`` option. + Configuring from Files ---------------------- diff --git a/docs/security.rst b/docs/security.rst index b68e909e..44c095ac 100644 --- a/docs/security.rst +++ b/docs/security.rst @@ -184,6 +184,9 @@ contains the same data. :: - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection + +.. _security-cookie: + Set-Cookie options ~~~~~~~~~~~~~~~~~~ @@ -194,19 +197,21 @@ They can be set on other cookies too. - ``Secure`` limits cookies to HTTPS traffic only. - ``HttpOnly`` protects the contents of cookies from being read with JavaScript. -- ``SameSite`` ensures that cookies can only be requested from the same - domain that created them. There are two possible values for the same-site - attribute: "Strict" and "Lax" +- ``SameSite`` restricts how cookies are sent with requests from + external sites. Can be set to ``'Lax'`` (recommended) or ``'Strict'``. + ``Lax`` prevents sending cookies with CSRF-prone requests from + external sites, such as submitting a form. ``Strict`` prevents sending + cookies with all external requests, including following regular links. :: app.config.update( SESSION_COOKIE_SECURE=True, SESSION_COOKIE_HTTPONLY=True, - SESSION_COOKIE_SAMESITE='Strict' + SESSION_COOKIE_SAMESITE='Lax', ) - response.set_cookie('username', 'flask', secure=True, httponly=True, samesite='Strict') + response.set_cookie('username', 'flask', secure=True, httponly=True, samesite='Lax') Specifying ``Expires`` or ``Max-Age`` options, will remove the cookie after the given time, or the current time plus the age, respectively. If neither @@ -239,6 +244,9 @@ values (or any values that need secure signatures). - https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie +.. _samesite_support: https://caniuse.com/#feat=same-site-cookie-attribute + + HTTP Public Key Pinning (HPKP) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/flask/sessions.py b/flask/sessions.py index eb028027..621f3f5e 100644 --- a/flask/sessions.py +++ b/flask/sessions.py @@ -250,9 +250,9 @@ class SessionInterface(object): return app.config['SESSION_COOKIE_SECURE'] def get_cookie_samesite(self, app): - """Returns "Strict", "Lax" or None if the cookie should use - samesite attribute. This currently just returns the value of - the ``SESSION_COOKIE_SAMESITE`` setting. + """Return ``'Strict'`` or ``'Lax'`` if the cookie should use the + ``SameSite`` attribute. This currently just returns the value of + the :data:`SESSION_COOKIE_SAMESITE` setting. """ return app.config['SESSION_COOKIE_SAMESITE'] diff --git a/tests/test_basic.py b/tests/test_basic.py index b0397ee6..0e55b52e 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -319,7 +319,7 @@ def test_session_using_session_settings(app, client): SESSION_COOKIE_DOMAIN='.example.com', SESSION_COOKIE_HTTPONLY=False, SESSION_COOKIE_SECURE=True, - SESSION_COOKIE_SAMESITE='Strict', + SESSION_COOKIE_SAMESITE='Lax', SESSION_COOKIE_PATH='/' ) @@ -338,41 +338,32 @@ def test_session_using_session_settings(app, client): def test_session_using_samesite_attribute(app, client): - app.config.update( - SERVER_NAME='www.example.com:8080', - APPLICATION_ROOT='/test', - SESSION_COOKIE_DOMAIN='.example.com', - SESSION_COOKIE_HTTPONLY=False, - SESSION_COOKIE_SECURE=True, - SESSION_COOKIE_SAMESITE='anyvalue', - SESSION_COOKIE_PATH='/' - ) - @app.route('/') def index(): flask.session['testing'] = 42 return 'Hello World' - # assert excption when samesite is not set to 'Strict', 'Lax' or None + app.config.update(SESSION_COOKIE_SAMESITE='invalid') + with pytest.raises(ValueError): - rv = client.get('/', 'http://www.example.com:8080/test/') + client.get('/') - # assert the samesite flag is not set in the cookie, when set to None app.config.update(SESSION_COOKIE_SAMESITE=None) - rv = client.get('/', 'http://www.example.com:8080/test/') + rv = client.get('/') cookie = rv.headers['set-cookie'].lower() assert 'samesite' not in cookie app.config.update(SESSION_COOKIE_SAMESITE='Strict') - rv = client.get('/', 'http://www.example.com:8080/test/') + rv = client.get('/') cookie = rv.headers['set-cookie'].lower() assert 'samesite=strict' in cookie app.config.update(SESSION_COOKIE_SAMESITE='Lax') - rv = client.get('/', 'http://www.example.com:8080/test/') + rv = client.get('/') cookie = rv.headers['set-cookie'].lower() assert 'samesite=lax' in cookie + def test_session_localhost_warning(recwarn, app, client): app.config.update( SERVER_NAME='localhost:5000',