Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you add csrf validation to pyramid?

Tags:

python

pyramid

I'm passing in a csrf_token for every post and xhr request and want to validate the token against the session csrf token. If they don't match, I throw a 401.

I've used the NewResponse subscriber in pyramid to inspect the request and validate the csrf token in the request params against the token in the session. The validation works but it still calls the view so it def does not work as it should.

Any suggestions on the proper way to do this?

@subscriber(NewResponse)
def new_response(event):
    """Check the csrf_token if the user is authenticated and the 
    request is a post or xhr req.
    """
request = event.request
response = event.response
user = getattr(request, 'user', None)
# For now all xhr request are csrf protected.
if (user and user.is_authenticated()) and \
   (request.method == "POST" or request.is_xhr) and \
    (not request.params.get('csrf_token') or \
    request.params.get('csrf_token') != unicode(request.session.get_csrf_token())):
    response.status = '401 Unauthorized'
    response.app_iter = []
like image 423
Joseph Estrada Avatar asked Jun 22 '11 03:06

Joseph Estrada


2 Answers

The NewResponse subscriber is called after your view is invoked.

You want to be using an event that is invoked earlier, for example NewRequest or ContextFound. In Pyramid 1.0, you'll need to use ContextFound to properly handle things because you cannot raise exceptions in NewRequest events (this is fixed in 1.1).

The way to do this with a ContextFound event is to register an exception view for HTTPException objects like this:

config.add_view(lambda ctx, req: ctx, 'pyramid.httpexceptions.HTTPException')

Basically this will return the exception as the response object when you raise it, which is perfectly valid for HTTPException objects which are valid Pyramid Response objects.

You can then register your event and deal with the CSRF validation:

@subscriber(ContextFound)
def csrf_validation_event(event):
    request = event.request
    user = getattr(request, 'user', None)
    csrf = request.params.get('csrf_token')
    if (request.method == 'POST' or request.is_xhr) and \
       (user and user.is_authenticated()) and \
       (csrf != unicode(request.session.get_csrf_token())):
        raise HTTPUnauthorized
like image 167
Michael Merickel Avatar answered Nov 15 '22 15:11

Michael Merickel


Pyramid contains its own CSRF validation, which is probably a better choice.

Given your session stored CSRF tokens, this would result in the following configuration:

from pyramid.csrf import SessionCSRFStoragePolicy

def includeme(config):
    # ...
    config.set_csrf_storage_policy(SessionCSRFStoragePolicy())
    config.set_default_csrf_options(require_csrf=True)
like image 37
pansen Avatar answered Nov 15 '22 16:11

pansen