Skip to content

Commit 8239ba5

Browse files
authored
Change config defaults for cookies. (#1236)
Best practice these days is to set ``secure=True``. This changes CSRF_COOKIE and TF_VALIDITY_COOKIE. Also - TF_VALIDITY_COOKIE name is now configurable. close #1228
2 parents 1a5ae5c + 7c6fba7 commit 8239ba5

6 files changed

Lines changed: 54 additions & 24 deletions

File tree

CHANGES.rst

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ Features & Improvements
1313
- (:issue:`1206`) Add support for refresh tokens. See :ref:`token_topic`
1414
- (:pr:`1233`) Change :py:data:`SECURITY_TOKEN_MAX_AGE` from an int to a timedelta.
1515
Also - change default from ``never expire`` to 15 minutes.
16-
- (:pr:`xx`) Change default LOGOUT_METHODS to be just ``"POST"``
16+
- (:pr:`1235`) Change default LOGOUT_METHODS to be just ``"POST"``
17+
- (:issue:`1228`) Change default ``csrf`` and ``tf_validity`` cookie config to ``secure=True``
18+
- (:issue:`1228`) The ``tf_validity`` cookie name is now configurable via :py:data:`SECURITY_TWO_FACTOR_VALIDITY_COOKIE_NAME`
1719

1820
Fixes
1921
+++++
@@ -35,6 +37,10 @@ Backwards Compatibility Concerns
3537
feature.
3638
- The default for :py:data:`SECURITY_LOGOUT_METHODS` has been changed to just
3739
allowing ``"POST"``. This is considered modern best practice.
40+
- The default configuration for :py:data:`SECURITY_TWO_FACTOR_VALIDITY_COOKIE`
41+
has been change to ``secure=True``.
42+
- The default configuration for :py:data:`SECURITY_CSRF_COOKIE` has been
43+
changed to ``secure=True``
3844

3945
Notes
4046
+++++

docs/configuration.rst

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -282,12 +282,15 @@ These configuration keys are used globally across all features.
282282
set a CSRF cookie.
283283
The complete set of parameters is described in Flask's `set_cookie`_ documentation.
284284

285-
Default: ``{"samesite": "Strict", "httponly": False, "secure": False}``
285+
Default: ``{"samesite": "Strict", "httponly": False, "secure": True}``
286286

287287
.. versionchanged:: 4.1.0
288288
The 'key' attribute was deprecated in favor of a separate configuration
289289
variable :data:`SECURITY_CSRF_COOKIE_NAME`.
290290

291+
.. versionchanged:: 5.9.0
292+
``secure`` default changed to ``True``
293+
291294
.. py:data:: SECURITY_CSRF_HEADER
292295
293296
The HTTP Header name that will contain the CSRF token. ``X-XSRF-Token``
@@ -1424,7 +1427,7 @@ Configuration related to the two-factor authentication feature.
14241427
.. py:data:: SECURITY_TWO_FACTOR_ALWAYS_VALIDATE
14251428
14261429
Specifies whether the application should require a two-factor code upon every login.
1427-
If set to ``False`` then the 2 values below are used to determine when
1430+
If set to ``False`` then the values below are used to determine when
14281431
a code is required. Note that this is cookie based - so a new browser
14291432
session will always require a fresh two-factor code.
14301433

@@ -1436,12 +1439,24 @@ Configuration related to the two-factor authentication feature.
14361439
Default: ``"30 Days"``.
14371440

14381441

1442+
.. py:data:: SECURITY_TWO_FACTOR_VALIDITY_COOKIE_NAME
1443+
1444+
Name for the two factor validity cookie.
1445+
1446+
Default: ``tf_validity``
1447+
1448+
.. versionadded:: 5.9.0
1449+
14391450
.. py:data:: SECURITY_TWO_FACTOR_VALIDITY_COOKIE
14401451
14411452
A dictionary containing the parameters of the two-factor validity cookie.
14421453
The complete set of parameters is described in Flask's `set_cookie`_ documentation.
14431454

1444-
Default: ``{'httponly': True, 'secure': False, 'samesite': None}``.
1455+
Default: ``{'httponly': True, 'secure': True, 'samesite': "Strict"}``.
1456+
1457+
1458+
.. versionchanged:: 5.9.0
1459+
``secure`` default changed to ``True``. ``samesite`` default changed to ``Strict``
14451460

14461461
.. py:data:: SECURITY_TWO_FACTOR_IMPLEMENTATIONS
14471462

flask_security/core.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -260,9 +260,10 @@
260260
"TWO_FACTOR_ALWAYS_VALIDATE": True,
261261
"TWO_FACTOR_LOGIN_VALIDITY": "30 days",
262262
"TWO_FACTOR_VALIDITY_SALT": "tf-validity-salt",
263+
"TWO_FACTOR_VALIDITY_COOKIE_NAME": "tf_validity",
263264
"TWO_FACTOR_VALIDITY_COOKIE": {
264265
"httponly": True,
265-
"secure": False,
266+
"secure": True,
266267
"samesite": "Strict",
267268
},
268269
"TWO_FACTOR_SETUP_SALT": "tf-setup-salt",
@@ -378,7 +379,7 @@
378379
"CSRF_COOKIE": {
379380
"samesite": "Strict",
380381
"httponly": False,
381-
"secure": False,
382+
"secure": True,
382383
},
383384
"CSRF_HEADER": "X-XSRF-Token",
384385
"CSRF_COOKIE_REFRESH_EACH_REQUEST": False,

flask_security/tf_plugin.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ def tf_verify_validity_token(fs_uniquifier: str) -> bool:
290290
291291
:param fs_uniquifier: The ``fs_uniquifier`` of the submitting user.
292292
"""
293-
token = request.cookies.get("tf_validity", default=None)
293+
token = request.cookies.get(cv("TWO_FACTOR_VALIDITY_COOKIE_NAME"), default=None)
294294
if token is None:
295295
return False
296296

@@ -302,15 +302,15 @@ def tf_verify_validity_token(fs_uniquifier: str) -> bool:
302302

303303

304304
def tf_set_validity_token_cookie(response: Response, token: str) -> Response:
305-
"""Sets the Two-Factor validity token for a specific user given that is
306-
configured and the user selects remember me
307-
308-
:param response: The response with which to set the set_cookie
309-
:param token: validity token
310-
"""
305+
"""Sets the Two-Factor validity cookie"""
311306
cookie_kwargs = cv("TWO_FACTOR_VALIDITY_COOKIE")
312307
max_age = int(get_within_delta("TWO_FACTOR_LOGIN_VALIDITY").total_seconds())
313-
response.set_cookie("tf_validity", value=token, max_age=max_age, **cookie_kwargs)
308+
response.set_cookie(
309+
cv("TWO_FACTOR_VALIDITY_COOKIE_NAME"),
310+
value=token,
311+
max_age=max_age,
312+
**cookie_kwargs,
313+
)
314314
# This is likely overkill since so far we only return this on a POST which is
315315
# unlikely to be cached.
316316
response.vary.add("Cookie")

tests/test_two_factor.py

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -91,37 +91,42 @@ def tf_in_session(session):
9191
)
9292

9393

94-
@pytest.mark.settings(two_factor_always_validate=False)
95-
def test_always_validate(app, client, get_message):
94+
@pytest.mark.settings(
95+
two_factor_always_validate=False, two_factor_validity_cookie_name="tfv"
96+
)
97+
def test_validity_cookie(app, client, get_message):
9698
tf_authenticate(app, client, remember=True)
97-
assert client.get_cookie("tf_validity")
99+
assert client.get_cookie("tfv")
98100

101+
# logout shouldn't delete cookie so we can log in again with just password
99102
logout(client)
100103

101104
data = dict(email="gal@lp.com", password="password")
102105
response = client.post("/login", data=data, follow_redirects=True)
103106
assert b"Welcome gal@lp.com" in response.data
104107
assert response.status_code == 200
105-
106108
logout(client)
109+
110+
# ensure the cookie is for a specific user
107111
data = dict(email="gal2@lp.com", password="password")
108112
response = client.post("/login", data=data, follow_redirects=True)
109113
assert b"Please enter your authentication code" in response.data
110114

111-
# make sure the cookie doesn't affect the JSON request
112-
client.delete_cookie("tf_validity")
113-
# Test JSON (this authenticates gal@lp.com)
115+
client.delete_cookie("tfv")
116+
117+
# Same tests as above with JSON (this authenticates gal@lp.com)
114118
tf_authenticate(app, client, json=True, remember=True)
119+
assert client.get_cookie("tfv")
115120
logout(client)
116121

122+
# shouldn't require tf
117123
data = dict(email="gal@lp.com", password="password")
118124
response = client.post(
119125
"/login",
120126
json=data,
121127
follow_redirects=True,
122128
)
123129
assert response.status_code == 200
124-
# verify logged in
125130
is_authenticated(client, get_message)
126131
logout(client)
127132

@@ -139,7 +144,10 @@ def test_always_validate(app, client, get_message):
139144

140145
@pytest.mark.settings(two_factor_always_validate=False)
141146
def test_do_not_remember_tf_validity(app, client):
147+
# If 'remember' not set during login - no cookie should be sent
148+
# and tf should be required every time.
142149
tf_authenticate(app, client)
150+
assert not client.get_cookie("tf_validity")
143151
logout(client)
144152

145153
data = dict(email="gal@lp.com", password="password")

tests/test_unified_signin.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1857,10 +1857,10 @@ def test_totp_generation(app, client, get_message):
18571857
)
18581858
def test_us_tf_validity(app, client, get_message):
18591859
us_tf_authenticate(app, client, remember=True)
1860-
assert client.get_cookie("tf_validity")
1860+
assert client.get_cookie(app.config["SECURITY_TWO_FACTOR_VALIDITY_COOKIE_NAME"])
18611861
logout(client)
18621862
# logout does NOT remove this cookie
1863-
assert client.get_cookie("tf_validity")
1863+
assert client.get_cookie(app.config["SECURITY_TWO_FACTOR_VALIDITY_COOKIE_NAME"])
18641864

18651865
# This time shouldn't require code
18661866
data = dict(identity="gal@lp.com", passcode="password")

0 commit comments

Comments
 (0)