I agree with Jacob on both points. +1 from me, especially since neither of these changes should require changes in application code which is already using the interface correctly.
-Paul On Tue, Jul 30, 2013 at 1:22 PM, Jacob Kaplan-Moss <[email protected]> wrote: > Hey Shai - > > I have no objections to this change. I think it's got a slight whiff of > security theatre, in that it *looks* like it adds more protection than it > *actually* does. However, I, too, have spent a ton of time talking auditors > down from "OMG Django is vulnerable to CSRF!" and I'd like to do less of > that. I like that rotating SECRET_KEY invalidates CSRF tokens. > > Time-limiting is a nice feature too, actually. Again the perceived security > is higher than the actual added security, but the general principle of > giving people more control is a good one. I'm sure there's some audit > checklist out there that has "CSRF tokens must not be valid for longer than > X hours" or something on it, and helping our users tick those boxes isn't > such a bad thing. > > So yeah, lukewarm praise from me at best, but since there's at least a bit > of real improvement here I see no reason this shouldn't go in. +1 from me. > > Jacob > > > On Sat, Jul 27, 2013 at 6:12 PM, Shai Berger <[email protected]> wrote: >> >> Hi everybody, >> >> TL;DR: A simple change can make Django's CSRF protection a little better; >> an >> additional, slightly less simple one, can also make it look better. >> >> Django's CSRF protection scheme is a bit unusual; unlike most such >> schemes, it >> does not rely on a value stored in the server that needs to be matched by >> a >> submitted token and is replaced with every submission, but rather on a >> constant value stored in a cookie. This generally works (for details of >> how >> and under what conditions exactly, see [1]), but has two minor problems: >> >> 1) It is unusual, and in particular diverges from what OWASP[2] >> recommends[3]; >> as a result, security analysts often think it is not secure. They have >> been >> proven wrong in all cases members of core are aware of, but proving it >> again >> and again is a nuisance, and there may be bad PR related to this. >> >> 2) It carries a "second-order" vulnerability: If your site has been >> compromised (XSS, Man-in-the-middle, or server compromise) then you become >> persistently vulnerable to CSRF. All of these vulnerabilities are way >> worse >> than CSRF and render all CSRF protection schemes worthless while they >> last; >> the point is *not* that they allow CSRF, but rather that they allow CSRF >> to be >> performed after the main hole has been plugged. This is because the >> attacker >> can use the main vulnerability to "steal", or even set, csrftoken cookie >> values, which they can then use later. After a successful attack of this >> magnitude, you need to reset the csrftoken cookies of all users, and this >> is >> neither obvious nor straightforward to do. >> >> Django's unique scheme does have two advantages over the more common >> solutions, which we would like to keep: >> >> 1) It is not tied to sessions, users, or site-stored per-user data, >> allowing >> CSRF protection to a wider range of users >> >> 2) It avoids the problem of having only one "current" token, which causes >> the >> submission of one form to invalidate forms open in other browser tabs. >> >> To improve on both problem issues, while keeping the advantages, I suggest >> the >> following modifications: >> >> a) Use a signed cookie for csrftoken -- using Django's existing signing >> facility[4], this means signing the cookie with the SECRET_KEY from the >> settings; so that an attacker cannot set arbitrary cookies, and changing >> the >> SECRET_KEY after a compromise immeiately invalidates csrftoken cookies. >> >> b) Optionally allowing time-limited CSRF tokens. Such tokens will be >> generated >> by adding a parameter of maximum age to the csrftoken tag, and by marking >> view >> methods (specifically with a decorator, or globally with a setting) as >> requiring timed tokens. When this is used, the posted token value will >> need to >> be different from the cookie value -- to keep advantage 2, the cookie will >> still be constant, and expiry time will only be present in the submitted >> token[5]. This method breaks the current way we do CSRF-protected AJAX, so >> it >> will likely stay optional (and opt-in). >> >> As you may guess, signing the cookie adds an actual iota of security. >> Adding >> expiry adds very little -- if an attacker has access to the cookie, they >> can >> usually just ask the site to generate valid tokens for them, so getting >> any >> real protection will require annoyingly short expiry times. But the fact >> that >> an attacker needs this extra step makes it a tiny bit harder for them and >> makes their actions a tiny bit more detectable; and having a constantly- >> changing CSRF token may make the whole thing look a little better to naive >> analysts. >> >> I had some help and guidance in drafting this proposal -- you can credit >> Donald Stufft, mostly, for any egregious blunder I didn't make. I am still >> responsible for the ones I did make. >> >> Your comments are welcome, >> >> Shai. >> >> >> [1] https://docs.djangoproject.com/en/dev/ref/contrib/csrf/ -- in >> particular, >> "how it works" and "limitations" >> >> [2] https://www.owasp.org >> >> [3] >> https://www.owasp.org/index.php/Cross-Site_Request_Forgery_%28CSRF%29_Prevention_Cheat_Sheet >> >> [4] https://docs.djangoproject.com/en/dev/topics/signing/ >> >> [5] django.core.signing.TimestampSigner signs content with the time of the >> signature, and then takes a max_age in its unsign() method; the suggested >> method would go the other way around, timestamping the token with the time >> of >> expiry, to allow checking without using data stored on the server (and to >> allow different forms to use different max-age values). >> >> -- >> You received this message because you are subscribed to the Google Groups >> "Django developers" group. >> To unsubscribe from this group and stop receiving emails from it, send an >> email to [email protected]. >> To post to this group, send email to [email protected]. >> Visit this group at http://groups.google.com/group/django-developers. >> For more options, visit https://groups.google.com/groups/opt_out. >> >> > > -- > You received this message because you are subscribed to the Google Groups > "Django developers" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to [email protected]. > To post to this group, send email to [email protected]. > Visit this group at http://groups.google.com/group/django-developers. > For more options, visit https://groups.google.com/groups/opt_out. > > -- You received this message because you are subscribed to the Google Groups "Django developers" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. To post to this group, send email to [email protected]. Visit this group at http://groups.google.com/group/django-developers. For more options, visit https://groups.google.com/groups/opt_out.
