# HG changeset patch # User Mads Kiilerich # Date 2019-01-04 03:51:38 # Node ID 5c5f0eb456816c8246d158be334eabaee806ddf5 # Parent 797883404f17c70f4a321205534eacc5c141297d auth: move CSRF checks from the optional LoginRequired to the more basic BaseController._before _before is not called for the CSRF-immune JSON-API controller and is thus a good place to check CSRF. This also apply CSRF protection to the login controller. The flag for needing CSRF checking is stored in the thread global request object when passed from __call__ to _before for regular controllers. It is thus also set for requests to the JSON-RPC controller, but not used. diff --git a/kallithea/lib/auth.py b/kallithea/lib/auth.py --- a/kallithea/lib/auth.py +++ b/kallithea/lib/auth.py @@ -675,18 +675,6 @@ class LoginRequired(object): loc = "%s:%s" % (controller.__class__.__name__, func.__name__) log.debug('Checking access for user %s @ %s', user, loc) - # CSRF protection: Whenever a request has ambient authority (whether - # through a session cookie or its origin IP address), it must include - # the correct token, unless the HTTP method is GET or HEAD (and thus - # guaranteed to be side effect free. In practice, the only situation - # where we allow side effects without ambient authority is when the - # authority comes from an API key; and that is handled above. - if user.authenticating_api_key is None and request.method not in ['GET', 'HEAD']: - token = request.POST.get(secure_form.token_key) - if not token or token != secure_form.authentication_token(): - log.error('CSRF check failed') - raise HTTPForbidden() - # regular user authentication if user.is_default_user: if self.allow_default_user: diff --git a/kallithea/lib/base.py b/kallithea/lib/base.py --- a/kallithea/lib/base.py +++ b/kallithea/lib/base.py @@ -327,6 +327,18 @@ class BaseController(TGController): """ _before is called before controller methods and after __call__ """ + if request.needs_csrf_check: + # CSRF protection: Whenever a request has ambient authority (whether + # through a session cookie or its origin IP address), it must include + # the correct token, unless the HTTP method is GET or HEAD (and thus + # guaranteed to be side effect free. In practice, the only situation + # where we allow side effects without ambient authority is when the + # authority comes from an API key; and that is handled above. + token = request.POST.get(secure_form.token_key) + if not token or token != secure_form.authentication_token(): + log.error('CSRF check failed') + raise webob.exc.HTTPForbidden() + c.kallithea_version = __version__ rc_config = Setting.get_app_settings() @@ -467,18 +479,15 @@ class BaseController(TGController): session.get('authuser'), ip_addr=ip_addr, ) + needs_csrf_check = request.method not in ['GET', 'HEAD'] else: dbuser = User.get_by_api_key(api_key) if dbuser is None: log.info('No db user found for authentication with API key ****%s from %s', api_key[-4:], ip_addr) - authuser = AuthUser.make( - dbuser=dbuser, - authenticating_api_key=api_key, - is_external_auth=True, - ip_addr=ip_addr, - ) + authuser = AuthUser.make(dbuser=dbuser, authenticating_api_key=api_key, is_external_auth=True, ip_addr=ip_addr) + needs_csrf_check = False # API key provides CSRF protection if authuser is None: log.info('No valid user found') @@ -487,6 +496,7 @@ class BaseController(TGController): # set globals for auth user request.authuser = authuser request.ip_addr = ip_addr + request.needs_csrf_check = needs_csrf_check log.info('IP: %s User: %s accessed %s', request.ip_addr, request.authuser,