CSRF changes - backwards incompatible

391 views
Skip to first unread message

Luke Plant

unread,
Oct 27, 2009, 9:03:14 AM10/27/09
to django-d...@googlegroups.com
Hi all,

For those following trunk, the CSRF changes have now landed (apart
from Simon's proposed refinements).

At first I thought this would be perfectly seamless, not requiring any
immediate action, and it therefore didn't warrant a note to django-
devs. However, there are circumstances when developers MUST update
their code immediately to avoid breakage.

If you have supplied custom templates to contrib views that accept
POST requests (e.g. auth login etc.), the template may need updating.
The steps needed are fully described in the docs, but in short:

Inside all <form method="POST"> elements, add {% csrf_token %}

That's it. Apologies to anyone who has already experience breakage
because of this.

This brings up another question - I don't know what our policy for
backwards compatibility with respect to templates is. Obviously if
you completely override an entire template for something like an admin
page, you cannot expect everything to keep working — otherwise it
would be impossible to add any new features to the admin. Also there
is the problem of inheritance etc. — do we guarantee that the
inheritance/include chain of all blocks will stay the same? Sometimes
you need to move anything around — 'refactor' if you will — and a
commitment not to change anything here would be painful (and it's not
what we've done so far, although we have minimized unnecessary
changes).

How much do we consider those who have overridden templates by setting
TEMPLATE_DIRS so that a custom template (e.g. admin/base_site.html) is
chosen over the default one? It's a common pattern, although in some
ways it could be considered a hack.

Is it different for views that have explicit hooks for customisation
(like a template_name keyword argument)? For the latter, I think the
view function docstring should document everything that will be in the
template context if we are making guarantees about compatibility. But
that would not have avoided the current problem of needing to insert
something extra.

What about CSS/javascript? I know there have been a number of changes
here which have caused breakage if people have overridden templates in
certain ways, but it seems unreasonable to require the admin CSS to
remain organized as it is now. The docs for this are in slight
disarray — I can only find some obsolete docs which say that they
don't apply any more.

It's actually quite hard to define backwards compatibility in this
area, much more so than with Python code.

Luke

--
Environmentalists are much too concerned with planet earth. Their
geocentric attitude prevents them from seeing the greater picture
-- lots of planets are much worse off than earth is.

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

rebus_

unread,
Oct 27, 2009, 9:07:14 AM10/27/09
to django-d...@googlegroups.com
2009/10/27 Luke Plant <L.Pla...@cantab.net>:

> If you have supplied custom templates to contrib views that accept
> POST requests (e.g. auth login etc.), the template may need updating.
> The steps needed are fully described in the docs, but in short:
>
>  Inside all <form method="POST"> elements, add {% csrf_token %}
>
> That's it. Apologies to anyone who has already experience breakage
> because of this.

And there are also some typos in guide:
====================================
http://docs.djangoproject.com/en/dev/ref/contrib/csrf/#legacy-method
====================================
In Django 1.1, the template tag did not exist. Instead, a post-
processing middleware that re-wrote POST forms to include the CRSF
token was used.

Use of the CsrfResponseMiddleware is not recommended because of the
performance hit it imposes, and because of a potential security
problem (see below). It can be used as an interim measure until
applications have been updated to use the {% crsf_token %} tag. It is
deprecated and will be removed in Django 1.4.
====================================
Typos:
... POST forms to include the CRSF token was used.
It should say "the CSRF token was used." and it's not {% crsf_token %}
but {% csrf_token %}

Davor

Luke Plant

unread,
Oct 27, 2009, 9:17:02 AM10/27/09
to django-d...@googlegroups.com
On Tuesday 27 October 2009 13:07:14 rebus_ wrote:

> And there are also some typos in guide:

Cheers! Fixed now. After this patch, I won't be sad if I never have
to type 'csrf' (or 'crsf') ever again :-) But unfortunately I will...

TheMaTrIx

unread,
Oct 27, 2009, 9:30:42 AM10/27/09
to Django developers
I don't understand something here. csrf is stated to be a option that
needs to be enabled if you wish to use it for views, yet I just ran a
trunk sync and boom, django-pages-cms is busted, without me enabling
anything.

Is it an always on feature or is something funky?

Luke Plant

unread,
Oct 27, 2009, 10:36:23 AM10/27/09
to django-d...@googlegroups.com
On Tuesday 27 October 2009 13:30:42 TheMaTrIx wrote:
> I don't understand something here. csrf is stated to be a option
> that needs to be enabled if you wish to use it for views, yet I
> just ran a trunk sync and boom, django-pages-cms is busted,
> without me enabling anything.

The CSRF protection is enabled by default in MIDDLEWARE_CLASSES, for
the unlikely scenario that you haven't set that setting. This is as
documented.

It is also enabled for all contrib views, as documented. That can
break if you using views from contrib apps, and have supplied custom
templates which don't have the CSRF token.

Beyond that, I'll need more details!

TheMaTrIx

unread,
Oct 27, 2009, 11:54:28 AM10/27/09
to Django developers
I fixed the django-pages-cms app by adding the csrf token tags into
the POST forms in the apps admin pages.

Luke Plant

unread,
Oct 27, 2009, 2:54:04 PM10/27/09
to django-d...@googlegroups.com
On Tuesday 27 October 2009 13:03:14 Luke Plant wrote:

> If you have supplied custom templates to contrib views that accept
> POST requests (e.g. auth login etc.), the template may need
> updating. The steps needed are fully described in the docs, but in
> short:
>
> Inside all <form method="POST"> elements, add {% csrf_token %}
>
> That's it.

It looks like this doesn't work for templates that are rendered by
inclusion_tag(). That's because they don't inherit the context —
something I didn't realise (probably because I haven't used
inclusion_tag).

There is a patch on http://code.djangoproject.com/ticket/12095 that
tries to address this.

I think the best solution is to fix inclusion_tag, rather than require
any complex changes for developers — otherwise they will get fed up
and decide "Django's CSRF sucks" and not use it. A nicer and more
general way of fixing inclusion_tag would be welcome. Adding tests
for this is a bit of a pain due to the way custom template tag
libraries need to be in an 'app'.

Jacob Kaplan-Moss

unread,
Oct 27, 2009, 3:19:12 PM10/27/09
to django-d...@googlegroups.com
On Tue, Oct 27, 2009 at 1:54 PM, Luke Plant <L.Pla...@cantab.net> wrote:
> There is a patch on http://code.djangoproject.com/ticket/12095 that
> tries to address this.

Repeating what I said on #django-dev this morning, I'm +1 on this
patch. Rending forms via an inclusion tag is a pretty common pattern
[1], and we shouldn't break it. I'm just fine special-casing the CSRF
token in inclusion_tag.

Jacob

[1] At least a couple of Django training programs, including the ones
I do, teach this technique. It's also in at least one book.

Sean Brant

unread,
Oct 27, 2009, 6:40:58 PM10/27/09
to django-d...@googlegroups.com
Interesting note. A co-worker of my has been working with the poll
tutorial for a couple days now and just got to part 3 which now
contains the {% csrf_token %} tag. He could not figure out how why he
was getting an error that the csrf_token tag could not be loaded.

I'm not sure how wide spread this will become, but I think for new
users it could be a problem. When asked what version of the doc he was
using he replied the one for trunk. He is running trunk but he did not
svn up for a few days hence his trunk falling behind.

Im not sure the best way to combat this problem, it was easily spotted
by me but only because I follow this list. Maybe the problem is people
that are learning should not be learning on trunk, which probably goes
without saying.

Anyways just thought I'd drop a real life user story seeing how this
feature just landed.

- Sean

vl4dt

unread,
Oct 30, 2009, 8:59:43 PM10/30/09
to Django developers
I just updated my django installation to the latest trunk.
I'm new do Django but I'm having this problem, I just ran the usual:
django-admin.py startproject testprj
cd testprj
(edited settings.py so it uses a sqlite3 database)
python manage.py syncdb
python manage.py runserver

Once the server is started I open
http://127.0.0.1:8000/
as usual, then I get this:

Traceback (most recent call last):

File "E:\Python26\lib\site-packages\django\core\servers
\basehttp.py", line 279, in run
self.result = application(self.environ, self.start_response)

File "E:\Python26\lib\site-packages\django\core\servers
\basehttp.py", line 651, in __call__
return self.application(environ, start_response)

File "E:\Python26\lib\site-packages\django\core\handlers\wsgi.py",
line 230, in __call__
self.load_middleware()

File "E:\Python26\lib\site-packages\django\core\handlers\base.py",
line 42, in load_middleware
raise exceptions.ImproperlyConfigured, 'Error importing middleware
%s: "%s"' % (mw_module, e)

ImproperlyConfigured: Error importing middleware
django.middleware.csrf: "No module named csrf"

Also, this happens even when I create an app, also on older projects I
have been working on before updating.
I also tried enabling the admin site after creating a test app and
this is the output:

Traceback (most recent call last):

File "E:\Python26\lib\site-packages\django\core\servers
\basehttp.py", line 279, in run
self.result = application(self.environ, self.start_response)

File "E:\Python26\lib\site-packages\django\core\servers
\basehttp.py", line 651, in __call__
return self.application(environ, start_response)

File "E:\Python26\lib\site-packages\django\core\handlers\wsgi.py",
line 230, in __call__
self.load_middleware()

File "E:\Python26\lib\site-packages\django\core\handlers\base.py",
line 42, in load_middleware
raise exceptions.ImproperlyConfigured, 'Error importing middleware
%s: "%s"' % (mw_module, e)

ImproperlyConfigured: Error importing middleware
django.middleware.csrf: "No module named csrf"

Same output.

What's wrong?

If I disable the csrf middleware the admin site complains, like this:

ImproperlyConfigured at /admin/
Error importing request processor module
django.core.context_processors: "No module named csrf"
...

I suspect the python path may be missing some location, still cant get
to it, any suggestions??

rebus_

unread,
Oct 30, 2009, 9:28:46 PM10/30/09
to django-d...@googlegroups.com
I would go with the idea that your settings are probably wrong. Just
tried trunk and it seems to work fine.

You should read:
http://docs.djangoproject.com/en/dev/ref/contrib/csrf/#upgrading-notes

Also, this is list is not for general Django troubleshooting but
rather for Django development.

Davor

Kegan Gan

unread,
Oct 30, 2009, 11:04:28 PM10/30/09
to Django developers
How does the csrf_token affect TestCase.client.post() ?

Luke Plant

unread,
Oct 31, 2009, 11:10:03 AM10/31/09
to django-d...@googlegroups.com
On Saturday 31 October 2009 03:04:28 Kegan Gan wrote:
> How does the csrf_token affect TestCase.client.post() ?

The token doesn't affect it directly. The HttpRequest class that
TestCase uses is hacked so that the middleware and csrf_protect
decorator don't actually reject requests which have the token.
Basically, it disables the protection for your tests, to make testing
of views much easier. All the cookies and tokens work just the same.

Luke

--
A common mistake that people make when trying to design something
completely foolproof is to underestimate the ingenuity of complete
fools. -- Douglas Adams

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

Reply all
Reply to author
Forward
0 new messages