Django

Code

Ticket #510 (closed: fixed)

Opened 3 years ago

Last modified 1 year ago

[patch] Defend admin against CSRF attacks

Reported by: Simon Willison Assigned to: adrian
Milestone: Component: Admin interface
Version: Keywords:
Cc: gdub@ece.utexas.edu Triage Stage: Unreviewed
Has patch: 1 Needs documentation: 0
Needs tests: 0 Patch needs improvement: 0

Description

Django's admin pages are curently vulnerable to CSRF attacks, as described here:

http://www.squarefree.com/securitytips/web-developers.html#CSRF

All POST forms in the admin should contain a hidden field with a shared secret that can be used to confirm the origin of the form.

Attachments

csrf-protection.patch (7.1 kB) - added by Simon Willison on 09/15/05 18:34:22.
Patch implementing CSRF protection for Django admin screens.
csrf-protection2.patch (7.5 kB) - added by Simon Willison on 09/16/05 08:36:01.
Improved patch; now uses module methods for token checking and creation.

Change History

09/14/05 11:31:34 changed by Simon Willison

My post about this to the developer mailing list: http://groups.google.com/group/django-developers/msg/74d0a8d633990a1e

09/14/05 11:49:45 changed by anonymous

Here's a way of defending the admin with an extra validator and a custom template tag.

Have a model built in to Django called csrf_tokens, with three fields:

tokenA long, randomly generated string.
issuedThe date and time that the token was issued.
user_idThe user to which the token was issued.

Define a custom template tag called {% csrf_token %}, which, when rendered, does the following:

  1. Creates a new random token and inserts it in to the csrf_tokens table. If user is available in the context, populate the user_id field.
  2. Displays a hidden form field with name="csrf_token" and the value set to the token.

The form view (probably in the validator logic) looks at the csrf_token of any submitted forms and checks that it is a record in the csrf_tokens table that was added for the same user (if the user field is set) within the last 10 minutes. If it finds one, it deletes that token - hence tokens can only ever be used once. If the token isn't there or is too old, the form is redisplayed with a suitable error message and the user has to resubmit it.

This should be secure. My only concern is this: would it be possible for an attacker to trick your browser in to making the initial submission with an incorrect token, then submit the form that was returned? I'm pretty sure the answer to that one is no.

09/15/05 18:34:22 changed by Simon Willison

  • attachment csrf-protection.patch added.

Patch implementing CSRF protection for Django admin screens.

09/15/05 18:40:10 changed by Simon Willison

  • type changed from defect to enhancement.
  • summary changed from Defend admin against CSRF attacks to [patch] Defend admin against CSRF attacks.

This patch implements CSRF protection for all Add, Change and Delete forms in Django's admin. It adds a new table to Django (defined in the auth model file) called auth_csrf_tokens, which stores csrf tokens. When ever you view an add, change or delete form a taken is created and saved in that table. When you submit the form, the token is deleted.

At the moment, there is no mechanism for clearing stale tokens out of the table. This needs to be done every day or so, by deleting tokens that are over a day old. Token accumulation is inevitable - to get to the delete screen for an item you must first visit its change screen, which will result in the creation of a stale token.

I've tested the patch and it appears to block all CSRF attacks.

09/16/05 08:36:01 changed by Simon Willison

  • attachment csrf-protection2.patch added.

Improved patch; now uses module methods for token checking and creation.

09/25/05 17:16:06 changed by jacob

  • milestone set to Version 1.0.

02/20/06 15:46:52 changed by anonymous

  • cc set to gdub@ece.utexas.edu.

03/24/06 17:06:46 changed by SmileyChris

Just looking at csrf-protection2.patch, CsrfTokenNode? should return XHTML:

class CsrfTokenNode(template.Node): 
    def render(self, context): 
        token = csrf.create_token(context['user'].id) 
        return '<input type="hidden" name="csrf_token" value="%s" />' % token.token

04/05/06 19:26:12 changed by SmileyChris

Do we really need to store these tokens in their own model? I propose the CSRF value should be stored in the session, set when the session is created and changed whenever tested against.

04/06/06 13:00:48 changed by lukeplant

I was prompted by SmileyChris?'s addition to add a link here to the CSRF middleware I wrote a while back. It uses a more lightweight approach that doesn't require storing anything in the database, and works for any POST request, not just the admin.

http://lukeplant.me.uk/resources/csrfmiddleware/

04/13/06 07:02:43 changed by mir@noris.de

Lukeplant's middleware works fine. The difference between this and the patch is:

  • the patch provides a way to enable csrf fields on a per-template base.
  • Lukeplant's middleware works out of the box for all forms automatically

I chose the middleware solution for myself ... either the middleware (into contrib?) or the patch should really be integrated into django.

05/09/06 18:03:28 changed by SmileyChris

Sweet, I see Luke's solution has just been added to contrib :)

05/31/06 23:01:12 changed by adrian

  • status changed from new to closed.
  • resolution set to fixed.

Fixed now that we have the CSRFMiddleware.

06/21/06 00:13:48 changed by anonymous

which happened in [2868].

01/17/07 16:12:17 changed by

  • milestone deleted.

Milestone Version 1.0 deleted


Add/Change #510 ([patch] Defend admin against CSRF attacks)




Change Properties
Action