-
Notifications
You must be signed in to change notification settings - Fork 154
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Improve CSRF and SPA (CSRF_COOKIE). (#972)
We used to set the CSRF_COOKIE (if configured) at the end of a successful authentication. For 2-factor that meant that /tf-validate needed to have the CSRF-HEADER set manually (as well as /login). There seems no reason not to set the CSRF-COOKIE on GET /login - just as we return the csrf_token - so that all endpoints can use the cookie if wanted (which is what many js frameworks do). There appeared to be no CSRF tests for logging in with unified sign in - now there is. closes #965
- Loading branch information
Showing
10 changed files
with
195 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1505,3 +1505,92 @@ def test_setup_csrf_header(app, client): | |
"tf-setup", json=dict(setup="disable"), headers={"X-CSRF-Token": csrf_token} | ||
) | ||
assert response.status_code == 200 | ||
|
||
|
||
@pytest.mark.csrf(csrfprotect=True) | ||
@pytest.mark.settings(CSRF_COOKIE_NAME="XSRF-Token") | ||
def test_csrf_2fa_login_cookie(app, client): | ||
# Use XSRF-Token cookie for entire login sequence | ||
sms_sender = SmsSenderFactory.createSender("test") | ||
response = client.get( | ||
"/login", data={}, headers={"Content-Type": "application/json"} | ||
) | ||
assert client.get_cookie("XSRF-Token") | ||
csrf_token = response.json["response"]["csrf_token"] | ||
assert csrf_token == client.get_cookie("XSRF-Token").value | ||
|
||
response = client.post( | ||
"/login", | ||
json=dict(email="[email protected]", password="password"), | ||
headers={ | ||
"Content-Type": "application/json", | ||
"X-CSRF-Token": client.get_cookie("XSRF-Token").value, | ||
}, | ||
) | ||
assert b'"code": 200' in response.data | ||
session = get_session(response) | ||
assert session["tf_state"] == "ready" | ||
|
||
assert sms_sender.get_count() == 1 | ||
code = sms_sender.messages[0].split()[-1] | ||
|
||
response = client.post( | ||
"/tf-validate", | ||
json=dict(code=code), | ||
headers={ | ||
"Content-Type": "application/json", | ||
"X-CSRF-Token": client.get_cookie("XSRF-Token").value, | ||
}, | ||
) | ||
assert response.status_code == 200 | ||
|
||
# verify original session csrf_token still works. | ||
response = client.post( | ||
"/json_auth", | ||
json=dict(label="label"), | ||
headers={"Content-Type": "application/json", "X-CSRF-Token": csrf_token}, | ||
) | ||
assert response.status_code == 200 | ||
|
||
# use XSRF_Cookie to send in csrf_token | ||
response = client.post( | ||
"/json_auth", | ||
json=dict(label="label"), | ||
headers={ | ||
"Content-Type": "application/json", | ||
"X-CSRF-Token": client.get_cookie("XSRF-Token").value, | ||
}, | ||
) | ||
assert response.status_code == 200 | ||
assert response.json["label"] == "label" | ||
|
||
|
||
@pytest.mark.csrf(ignore_unauth=True, csrfprotect=True) | ||
@pytest.mark.settings(CSRF_COOKIE_NAME="XSRF-Token") | ||
def test_csrf_2fa_nounauth_cookie(app, client): | ||
# use CSRF cookie when ignoring unauth endpoints | ||
sms_sender = SmsSenderFactory.createSender("test") | ||
response = client.post( | ||
"/login", | ||
json=dict(email="[email protected]", password="password"), | ||
headers={"Content-Type": "application/json"}, | ||
) | ||
|
||
code = sms_sender.messages[0].split()[-1] | ||
response = client.post( | ||
"/tf-validate", | ||
json=dict(code=code), | ||
headers={"Content-Type": "application/json"}, | ||
) | ||
assert response.status_code == 200 | ||
|
||
response = client.post( | ||
"/json_auth", | ||
json=dict(label="label"), | ||
headers={ | ||
"Content-Type": "application/json", | ||
"X-CSRF-Token": client.get_cookie("XSRF-Token").value, | ||
}, | ||
) | ||
assert response.status_code == 200 | ||
assert response.json["label"] == "label" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -29,6 +29,7 @@ | |
check_location, | ||
check_xlation, | ||
get_form_action, | ||
get_session, | ||
is_authenticated, | ||
logout, | ||
reset_fresh, | ||
|
@@ -2214,3 +2215,71 @@ def test_empty_password_xlate(app, client, get_message): | |
).encode() | ||
in response.data | ||
) | ||
|
||
|
||
@pytest.mark.two_factor() | ||
@pytest.mark.csrf(csrfprotect=True) | ||
@pytest.mark.settings(CSRF_COOKIE_NAME="XSRF-Token") | ||
def test_csrf_2fa_us_cookie(app, client): | ||
# Use XSRF-Token cookie for entire login sequence | ||
sms_sender = SmsSenderFactory.createSender("test") | ||
response = client.get( | ||
"/us-signin", data={}, headers={"Content-Type": "application/json"} | ||
) | ||
assert client.get_cookie("XSRF-Token") | ||
csrf_token = response.json["response"]["csrf_token"] | ||
assert csrf_token == client.get_cookie("XSRF-Token").value | ||
|
||
# verify requires CSRF | ||
response = client.post( | ||
"/us-signin", | ||
json=dict(identity="[email protected]", passcode="password"), | ||
headers={"Content-Type": "application/json"}, | ||
) | ||
assert response.status_code == 400 | ||
assert response.json["response"]["errors"][0] == "The CSRF token is missing." | ||
|
||
response = client.post( | ||
"/us-signin", | ||
json=dict(identity="[email protected]", passcode="password"), | ||
headers={ | ||
"Content-Type": "application/json", | ||
"X-CSRF-Token": client.get_cookie("XSRF-Token").value, | ||
}, | ||
) | ||
assert b'"code": 200' in response.data | ||
session = get_session(response) | ||
assert session["tf_state"] == "ready" | ||
|
||
assert sms_sender.get_count() == 1 | ||
code = sms_sender.messages[0].split()[-1] | ||
|
||
response = client.post( | ||
"/tf-validate", | ||
json=dict(code=code), | ||
headers={ | ||
"Content-Type": "application/json", | ||
"X-CSRF-Token": client.get_cookie("XSRF-Token").value, | ||
}, | ||
) | ||
assert response.status_code == 200 | ||
|
||
# verify original session csrf_token still works. | ||
response = client.post( | ||
"/json_auth", | ||
json=dict(label="label"), | ||
headers={"Content-Type": "application/json", "X-CSRF-Token": csrf_token}, | ||
) | ||
assert response.status_code == 200 | ||
|
||
# use XSRF_Cookie to send in csrf_token | ||
response = client.post( | ||
"/json_auth", | ||
json=dict(label="label"), | ||
headers={ | ||
"Content-Type": "application/json", | ||
"X-CSRF-Token": client.get_cookie("XSRF-Token").value, | ||
}, | ||
) | ||
assert response.status_code == 200 | ||
assert response.json["label"] == "label" |