From 35d700eb2077f231cd69dedb6ea3811669b27c80 Mon Sep 17 00:00:00 2001 From: djrobstep Date: Wed, 16 Nov 2016 12:08:41 +1100 Subject: [PATCH] alternative csrf (#66) * alternative csrf * use response module to mock http requests --- dmutils/csrf.py | 27 ++++++++++--- dmutils/flask_init.py | 8 ++-- react/render_server.py | 6 ++- requirements_for_test.txt | 1 + tests/test_flask_init.py | 45 ++++++++++++++++++--- tests/test_logging.py | 42 +++++++++---------- tests/test_render_server.py | 80 ++++++++++++++++++------------------- 7 files changed, 131 insertions(+), 78 deletions(-) diff --git a/dmutils/csrf.py b/dmutils/csrf.py index bb8d8e0f..4b737e6c 100644 --- a/dmutils/csrf.py +++ b/dmutils/csrf.py @@ -1,9 +1,10 @@ import os import binascii -from flask import session, request +from flask import session, request, current_app TOKEN = '_csrf_token' +OLD_TOKEN = 'csrf_token' REACT_HEADER_NAME = 'X-CSRFToken' @@ -17,8 +18,22 @@ def get_csrf_token(): return session[TOKEN] -def check_valid_header_csrf(): - try: - return session[TOKEN] == request.headers[REACT_HEADER_NAME] - except KeyError: - return False +def check_valid_csrf(): + if not current_app.config.get('CSRF_ENABLED') and not current_app.config.get('CSRF_FAKED'): + return True + + tokens_received = [ + request.form.get(OLD_TOKEN, None), + request.form.get(TOKEN, None), + request.headers.get(REACT_HEADER_NAME, None) + ] + tokens_received = set(filter(None, tokens_received)) + + tokens_from_session = [ + session.get(TOKEN, None), + session.get(OLD_TOKEN, None) + ] + tokens_from_session = set(filter(None, tokens_from_session)) + + intersect = tokens_received.intersection(tokens_from_session) + return bool(intersect) diff --git a/dmutils/flask_init.py b/dmutils/flask_init.py index 958c925d..e9ee4927 100644 --- a/dmutils/flask_init.py +++ b/dmutils/flask_init.py @@ -16,7 +16,7 @@ from dmutils import terms_of_use from dmutils.forms import is_csrf_token_valid -from .csrf import check_valid_header_csrf +from .csrf import check_valid_csrf def init_app( @@ -99,10 +99,10 @@ def load_user(user_id): @application.before_request def check_csrf_token(): if request.method in ('POST', 'PATCH', 'PUT', 'DELETE'): - flask_csrf_valid = is_csrf_token_valid() - react_csrf_valid = check_valid_header_csrf() + old_csrf_valid = is_csrf_token_valid() + new_csrf_valid = check_valid_csrf() - if not (flask_csrf_valid or react_csrf_valid): + if not (old_csrf_valid or new_csrf_valid): current_app.logger.info( u'csrf.invalid_token: Aborting request, user_id: {user_id}', extra={'user_id': session.get('user_id', '