Browse Source

New Feature: Added Support for cookie's SameSite attribute.

pull/2607/head
Fadhel_Chaabane 7 years ago
parent
commit
a1d9ebe4ab
  1. 9
      docs/config.rst
  2. 6
      docs/security.rst
  3. 1
      flask/app.py
  4. 11
      flask/sessions.py
  5. 38
      tests/test_basic.py

9
docs/config.rst

@ -208,6 +208,14 @@ The following configuration values are used internally by Flask:
Default: ``False`` Default: ``False``
.. 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.
Default: ``None``
.. py:data:: PERMANENT_SESSION_LIFETIME .. py:data:: PERMANENT_SESSION_LIFETIME
If ``session.permanent`` is true, the cookie's expiration will be set this If ``session.permanent`` is true, the cookie's expiration will be set this
@ -635,4 +643,3 @@ Example usage for both::
# or via open_instance_resource: # or via open_instance_resource:
with app.open_instance_resource('application.cfg') as f: with app.open_instance_resource('application.cfg') as f:
config = f.read() config = f.read()

6
docs/security.rst

@ -195,16 +195,18 @@ They can be set on other cookies too.
- ``HttpOnly`` protects the contents of cookies from being read with - ``HttpOnly`` protects the contents of cookies from being read with
JavaScript. JavaScript.
- ``SameSite`` ensures that cookies can only be requested from the same - ``SameSite`` ensures that cookies can only be requested from the same
domain that created them. It is not supported by Flask yet. domain that created them. There are two possible values for the same-site
attribute: "Strict" and "Lax"
:: ::
app.config.update( app.config.update(
SESSION_COOKIE_SECURE=True, SESSION_COOKIE_SECURE=True,
SESSION_COOKIE_HTTPONLY=True, SESSION_COOKIE_HTTPONLY=True,
SESSION_COOKIE_SAMESITE='Strict'
) )
response.set_cookie('username', 'flask', secure=True, httponly=True) response.set_cookie('username', 'flask', secure=True, httponly=True, samesite='Strict')
Specifying ``Expires`` or ``Max-Age`` options, will remove the cookie after Specifying ``Expires`` or ``Max-Age`` options, will remove the cookie after
the given time, or the current time plus the age, respectively. If neither the given time, or the current time plus the age, respectively. If neither

1
flask/app.py

@ -284,6 +284,7 @@ class Flask(_PackageBoundObject):
'SESSION_COOKIE_PATH': None, 'SESSION_COOKIE_PATH': None,
'SESSION_COOKIE_HTTPONLY': True, 'SESSION_COOKIE_HTTPONLY': True,
'SESSION_COOKIE_SECURE': False, 'SESSION_COOKIE_SECURE': False,
'SESSION_COOKIE_SAMESITE': None,
'SESSION_REFRESH_EACH_REQUEST': True, 'SESSION_REFRESH_EACH_REQUEST': True,
'MAX_CONTENT_LENGTH': None, 'MAX_CONTENT_LENGTH': None,
'SEND_FILE_MAX_AGE_DEFAULT': timedelta(hours=12), 'SEND_FILE_MAX_AGE_DEFAULT': timedelta(hours=12),

11
flask/sessions.py

@ -249,6 +249,13 @@ class SessionInterface(object):
""" """
return app.config['SESSION_COOKIE_SECURE'] 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 app.config['SESSION_COOKIE_SAMESITE']
def get_expiration_time(self, app, session): def get_expiration_time(self, app, session):
"""A helper method that returns an expiration date for the session """A helper method that returns an expiration date for the session
or ``None`` if the session is linked to the browser session. The or ``None`` if the session is linked to the browser session. The
@ -362,6 +369,7 @@ class SecureCookieSessionInterface(SessionInterface):
httponly = self.get_cookie_httponly(app) httponly = self.get_cookie_httponly(app)
secure = self.get_cookie_secure(app) secure = self.get_cookie_secure(app)
samesite = self.get_cookie_samesite(app)
expires = self.get_expiration_time(app, session) expires = self.get_expiration_time(app, session)
val = self.get_signing_serializer(app).dumps(dict(session)) val = self.get_signing_serializer(app).dumps(dict(session))
response.set_cookie( response.set_cookie(
@ -371,5 +379,6 @@ class SecureCookieSessionInterface(SessionInterface):
httponly=httponly, httponly=httponly,
domain=domain, domain=domain,
path=path, path=path,
secure=secure secure=secure,
samesite=samesite
) )

38
tests/test_basic.py

@ -319,6 +319,7 @@ def test_session_using_session_settings(app, client):
SESSION_COOKIE_DOMAIN='.example.com', SESSION_COOKIE_DOMAIN='.example.com',
SESSION_COOKIE_HTTPONLY=False, SESSION_COOKIE_HTTPONLY=False,
SESSION_COOKIE_SECURE=True, SESSION_COOKIE_SECURE=True,
SESSION_COOKIE_SAMESITE='Strict',
SESSION_COOKIE_PATH='/' SESSION_COOKIE_PATH='/'
) )
@ -333,8 +334,45 @@ def test_session_using_session_settings(app, client):
assert 'path=/' in cookie assert 'path=/' in cookie
assert 'secure' in cookie assert 'secure' in cookie
assert 'httponly' not in cookie assert 'httponly' not in cookie
assert 'samesite' in cookie
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
with pytest.raises(ValueError):
rv = client.get('/', 'http://www.example.com:8080/test/')
# 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/')
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/')
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/')
cookie = rv.headers['set-cookie'].lower()
assert 'samesite=lax' in cookie
def test_session_localhost_warning(recwarn, app, client): def test_session_localhost_warning(recwarn, app, client):
app.config.update( app.config.update(
SERVER_NAME='localhost:5000', SERVER_NAME='localhost:5000',

Loading…
Cancel
Save