Have Quay always use an OAuth-specific CSRF token

This change ensures that we always store and then check the contents of the OAuth `state` argument against a session-stored CSRF token.

Fixes https://www.pivotaltracker.com/story/show/135803615
This commit is contained in:
Joseph Schorr 2016-12-08 16:11:57 -05:00
parent 34f2ddce87
commit ff52fde8a5
8 changed files with 72 additions and 48 deletions

View file

@ -12,31 +12,46 @@ from util.http import abort
logger = logging.getLogger(__name__)
OAUTH_CSRF_TOKEN_NAME = '_oauth_csrf_token'
_QUAY_CSRF_TOKEN_NAME = '_csrf_token'
def generate_csrf_token():
if '_csrf_token' not in session:
session['_csrf_token'] = base64.b64encode(os.urandom(48))
def generate_csrf_token(session_token_name=_QUAY_CSRF_TOKEN_NAME):
""" If not present in the session, generates a new CSRF token with the given name
and places it into the session. Returns the generated token.
"""
if session_token_name not in session:
session[session_token_name] = base64.b64encode(os.urandom(48))
return session['_csrf_token']
return session[session_token_name]
def verify_csrf():
token = session.get('_csrf_token', None)
found_token = request.values.get('_csrf_token', None)
def verify_csrf(session_token_name=_QUAY_CSRF_TOKEN_NAME,
request_token_name=_QUAY_CSRF_TOKEN_NAME):
""" Verifies that the CSRF token with the given name is found in the session and
that the matching token is found in the request args or values.
"""
token = session.get(session_token_name, None)
found_token = request.values.get(request_token_name, None)
if not token or token != found_token:
msg = 'CSRF Failure. Session token was %s and request token was %s'
logger.error(msg, token, found_token)
msg = 'CSRF Failure. Session token (%s) was %s and request token (%s) was %s'
logger.error(msg, session_token_name, token, request_token_name, found_token)
abort(403, message='CSRF token was invalid or missing.')
def csrf_protect(func):
@wraps(func)
def wrapper(*args, **kwargs):
oauth_token = get_validated_oauth_token()
if oauth_token is None and request.method != "GET" and request.method != "HEAD":
verify_csrf()
return func(*args, **kwargs)
return wrapper
def csrf_protect(session_token_name=_QUAY_CSRF_TOKEN_NAME,
request_token_name=_QUAY_CSRF_TOKEN_NAME,
all_methods=False):
def inner(func):
@wraps(func)
def wrapper(*args, **kwargs):
oauth_token = get_validated_oauth_token()
if oauth_token is None:
if all_methods or (request.method != "GET" and request.method != "HEAD"):
verify_csrf(session_token_name, request_token_name)
return func(*args, **kwargs)
return wrapper
return inner
app.jinja_env.globals['csrf_token'] = generate_csrf_token