Hi all,

I'm thinking about 'championing' this thing, having just done a bit 
more work on the existing CsrfMiddleware [1], and I've done some more 
thinking about the different issues. LONG email, sorry, this is quite 
complex stuff.

== First ==
Simon suggested that the current middleware displays scary messages 
when people's sessions' expire, but that doesn't seem to be the case 
(normally).  The middleware simply checks for a session cookie and 
corresponding token, and only if they don't match does it throws a 
nasty page back.  The token itself does not expire, and even if the 
session has, the middleware doesn't know and doesn't care.  The 
*only* way I have succeeded in getting to the scary "Cross Site 
Request Forgery detected" message is like this:

 - open a page that returns a POST form (either in a view that
   requires authentication or one that doesn't).
 - aquire a new session *in a different tab*.  This might mean:
   1) you had no session when you opened the first page, and you
      log in in a different tab.
   2) you manually delete the session cookie, and do something
      that triggers a new sesssion
   3) you are doing something in a different tab, your session
      expires by itself and you are given a new one.
 - in the first tab, try to submit the form. It fails because the
   token in the page disagrees with the new session cookie.

Note that:
 - logging out and logging in in another tab is not enough (unless
   this causes a new session to be created).
 - waiting for the cookie to expire, or deleting the session from the 
   database is not enough.
In both these cases, you will be presented by the normal 'your session 
has expired' screen for views that require authentication, or you 
will simply carry on as normal if the view doesn't require 
authentication.

The above cases that trigger the screen seem to be quite unusual - 
I've never seen it myself when actually using a site with the 
CsrfMiddleware installed.

So what I'm saying is that the 'view protection' part of the 
middleware seems to work well enough for the vast majority of cases.  
If necessary we could always allow the error page to be customised 
via a setting (i.e. allow for a dotted path to a view function), and 
we could improve the default implementation to do something sensible, 
like display a message and then redirect to the original page after 5 
seconds.

I like the middleware for this part, because it means you are safe by 
default, and it works for any POST form -- no special action 
required.

Note that the recent changes to this middleware allow you to 
selectively turn it off for chosen views using a decorator.

== Second ==
There is the issue of how to insert a CSRF token into the forms.  Two 
proposals:

 - SafeForm
   - this takes a request, so it can generate the required field,
     removing the need for the 'html munging' part of CsrfMiddleware.
   - it could also have a validate method that would remove the need
     for the 'view protection' part of CsrfMiddleware
   - If the developer is manually specifying each field in their forms
     rather than using form.as_table() etc, they will have to edit
     the template.
   
 - a template tag
   - it would require using django.core.context_processors.request
   - it would depend on the 'view protection' part of CsrfMiddleware
   - it would always require changing the template to insert this tag
   - the major advantage is that it can be used with any POST form,
     so that no matter how your form works, there is one way to sort
     out the CSRF problem - add a template tag (and ensure the 
     middleware is on).

== Third ==
There is the 'login CSRF' problem.
One solution:
 - simply start a session whenever a login page is accessed if there 
   is no session already, and then use the normal mechanism above.
   - There are some issues with this in terms of implementation [2]
   - bots that hit the login page will generate sessions.

Second solution:
 - have a special 'login cookie' - a randomly generated id, not stored
   in the database, that is accompanied by a corresponding token.
   Requires some custom logic in every login view (you have to  
   generate the cookie, add it to the response, and also tell the 
   form/template about the value of the cookie/token).
   It may be possible to abstract some of this out into helper
   functions.

Third solution:
 - Add the functionality of the second solution to SafeForm.  It would
   require SafeForm to be able to set cookies and therefore to access
   the response, so the API gets a bit ugly.

== Conclusion ==

At the moment, once you've factored everything in, I think 'view 
middleware' + template tag is the way to go, with some more custom 
solution for login CSRF.  The SafeForm ends up having an unwieldly 
API, which means it won't be used or could be used incorrectly, it 
will often require changing a template anyway, and it's specific to 
Django forms.  The template tag solution would basically require a 
single line being added to the template for each form (plus some 
settings, once).

I also suggest we add CsrfMiddleware or CsrfViewMiddleware to the 
default middleware and put a note about it in the release notes.

Regards,

Luke

== Notes ==

[1] http://code.djangoproject.com/changeset/9554
    - automatic exceptions for AJAX
    - manual exceptions possible
    http://code.djangoproject.com/changeset/9553
    - split middleware into two components.

[2] It will mean:
  1) always doing a redirect when you display a login form: you
     have to create a new session, send the session cookie with a
     HttpRedirectResponse, then in the next request create a form that
     has the csrf token in it.  Decorators like @staff_member_required
     would need changing.
  OR
  2) creating a new session and returning a form with a csrf token at
     the same time.  The mechanism that generates the token will need 
     to be aware that the session ID might not be in
     request.COOKIES[SESSION_COOKIE_NAME], but in request.session.??


-- 
"Outside of a dog, a book is a man's best friend... inside of a dog, 
it's too dark to read."

Luke Plant || http://lukeplant.me.uk/

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Django developers" group.
To post to this group, send email to django-developers@googlegroups.com
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/django-developers?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to