Rotate the CSRF token on every request

674 views
Skip to first unread message

ibrw1...@gmail.com

unread,
Oct 31, 2014, 10:53:31 AM10/31/14
to django...@googlegroups.com
Hi all,

I am in the process right now of working on a web portal where we want to rotate the csrf token on each request. We intend to have a new token each time for optimal security. I was hoping someone might know more about this than I do because I've run into some difficulties that were not expected. We are using Django 1.5.7. Each request runs through the django.middleware.csrf.py  CsrfViewMIddleware middleware and a middleware we have added which should be rotating the token each time using the rotate_token function from django.middleware.csrf.py. The problem we have observed is that each time the token does rotate but then is reset in the process_view function in CsrfViewMiddleware. I modified the first few lines of CsrfViewMiddleware's process_request function slightly and found the token will now rotate as I had intended for each request.

Modified Code in django.middleware.csrf.py from CsrfViewMiddleware:

   def process_view(self, request, callback, callback_args, callback_kwargs):

        if getattr(request, 'csrf_processing_done', False):
            return None

        csrf_token = request.META['CSRF_COOKIE']     #CSRF_COOKIE is being rotating by our custom middleware
        if csrf_token == None:
            # Generate token and store it in the request, so it's
            # available to the view.
            request.META["CSRF_COOKIE"] = _get_new_csrf_key()


Django's Standard Code:

    def process_view(self, request, callback, callback_args, callback_kwargs):

        if getattr(request, 'csrf_processing_done', False):
            return None

        try:
            csrf_token = _sanitize_token(
                request.COOKIES[settings.CSRF_COOKIE_NAME])
            # Use same token next time
            request.META['CSRF_COOKIE'] = csrf_token
        except KeyError:
            csrf_token = None
            # Generate token and store it in the request, so it's
            # available to the view.
            request.META["CSRF_COOKIE"] = _get_new_csrf_key()


 I think there are a few more things that should be done here like a _sanitize_token call but this is the basic idea. The problem is I can't just modify the Django code because I don't understand the full set of possible side effects.  

Does anyone know:
1. The history of why the Django developers have set up the code in this fashion.
2. Any side effects that should be expected from making a change like the above modified Django code.
3. A way to do this without modifying the Django base code.

Also, it seems like a version of CsrfViewMiddleware that is conducive to rotating on each request would be helpful as part of Django's base code. Is a change like this worth submitting a pull request to the Django repository?

Thanks for any help in advance,
ibrw100000
 

Zach Borboa

unread,
Nov 1, 2014, 11:32:14 AM11/1/14
to django...@googlegroups.com
Rotating the CSRF token on every request is probably not a great idea. Tokens will become invalidated when multiple tabs are open.

Tim Chase

unread,
Nov 1, 2014, 2:42:50 PM11/1/14
to django...@googlegroups.com, zachb...@gmail.com
On 2014-11-01 08:32, Zach Borboa wrote:
> Rotating the CSRF token on every request is probably not a great
> idea. Tokens will become invalidated when multiple tabs are open.

I've used sites that do this and it infuriates me to no limit.
Unless absolutely mandated to use them for $JOB, it's a quick way to
get me to abandon a site.

-tkc


Collin Anderson

unread,
Nov 2, 2014, 8:31:25 PM11/2/14
to django...@googlegroups.com
Hello,

You could try setting a new CSRF token using javascript every time a form is submitted. Something like:
// Change this selector so it doesn't apply to forms with off-site actions.
$
(document).on('submit', 'form[method=post]', function(){
 
for(var c = ''; c.length < 32;) c += Math.random().toString(36).substr(2, 1)
 
// You could set the expiration time very short here, since you only need it for this request.
  document
.cookie = 'csrftoken=' + c + '; path=/'
 
if(!this.csrfmiddlewaretoken) $(this).append('<input type="hidden" name="csrfmiddlewaretoken">')
  $
(this.csrfmiddlewaretoken).val(c)
})
It should work correctly for multiple tabs, and unlike the django login form, even survive the back button.

Collin

Reply all
Reply to author
Forward
0 new messages