File CVE-2021-21241-GET-reqs-auth-token.patch of Package python-Flask-Security-Too
From 3442e0b73532634e1b8b81d60038301ebc0707fd Mon Sep 17 00:00:00 2001
From: jwag956 <jwag.wagner@gmail.com>
Date: Fri, 1 Jan 2021 19:25:53 -0800
Subject: [PATCH] Fix security vuln - GET on /login or /change could reveal
authentication token with no CSRF checks.
GETs no longer return the auth token.
closes: #421
---
flask_security/views.py | 23 +++++++++++++----------
tests/test_common.py | 23 +++++++++++++++++++++++
2 files changed, 36 insertions(+), 10 deletions(-)
--- a/flask_security/views.py
+++ b/flask_security/views.py
@@ -182,13 +182,14 @@ def login():
login_user(form.user, remember=remember_me, authn_via=["password"])
after_this_request(_commit)
- if not _security._want_json(request):
- return redirect(get_post_login_redirect())
+ if _security._want_json(request):
+ return base_render_json(form, include_auth_token=True)
+ return redirect(get_post_login_redirect())
if _security._want_json(request):
if current_user.is_authenticated:
form.user = current_user
- return base_render_json(form, include_auth_token=True)
+ return base_render_json(form)
if current_user.is_authenticated:
# Basically a no-op if authenticated - just perform the same
@@ -625,16 +626,18 @@ def change_password():
if form.validate_on_submit():
after_this_request(_commit)
change_user_password(current_user._get_current_object(), form.new_password.data)
- if not _security._want_json(request):
- do_flash(*get_message("PASSWORD_CHANGE"))
- return redirect(
- get_url(_security.post_change_view)
- or get_url(_security.post_login_view)
- )
+ if _security._want_json(request):
+ form.user = current_user
+ return base_render_json(form, include_auth_token=True)
+
+ do_flash(*get_message("PASSWORD_CHANGE"))
+ return redirect(
+ get_url(_security.post_change_view) or get_url(_security.post_login_view)
+ )
if _security._want_json(request):
form.user = current_user
- return base_render_json(form, include_auth_token=True)
+ return base_render_json(form)
return _security.render_template(
config_value("CHANGE_PASSWORD_TEMPLATE"),
--- a/tests/test_common.py
+++ b/tests/test_common.py
@@ -724,3 +724,26 @@ def test_session_query(in_app_context):
assert response.status_code == 200
end_nqueries = get_num_queries(app.security.datastore)
assert current_nqueries is None or end_nqueries == (current_nqueries + 2)
+
+
+@pytest.mark.changeable()
+def test_no_get_auth_token(app, client):
+ # Test that GETs don't return an auth token. This is a security issue since
+ # GETs aren't protected with CSRF
+ authenticate(client)
+ response = client.get(
+ "/login?include_auth_token", headers={"Content-Type": "application/json"}
+ )
+ assert "authentication_token" not in response.json["response"]["user"]
+
+ data = dict(
+ password="password",
+ new_password="new strong password",
+ new_password_confirm="new strong password",
+ )
+ response = client.get(
+ "/change?include_auth_token",
+ json=data,
+ headers={"Content-Type": "application/json"},
+ )
+ assert "authentication_token" not in response.json["response"]["user"]