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:
parent
34f2ddce87
commit
ff52fde8a5
8 changed files with 72 additions and 48 deletions
|
@ -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
|
||||
|
|
Reference in a new issue