= CSRF Protection = This page aims to document and discuss CSRF protection for Django. == Background == For general discussion of the CSRF issue see: * [http://en.wikipedia.org/wiki/CSRF Wikipedia on CSRF] * [http://www.adambarth.com/papers/2008/barth-jackson-mitchell-b.pdf Robust Defences for Cross-Site Forgery (Barth, Jackson, Mitchell)] - very important paper, terminology used here reflects the terminology of that paper. == Discussions == For the record, the most relevant discussion on django-developers are here, most recent first, but this page attempts to supersede them all by gathering the conclusions. * Discussion of #9977 patch csrf_template_tag_14.diff - http://groups.google.com/group/django-developers/browse_thread/thread/c23f556b88cedbc7?pli=1 * Luke Plant's proposal - http://groups.google.com/group/django-developers/browse_thread/thread/ae525f270ed46933/c338b050f43741b2?lnk=gst * Simon Willison's proposal - SafeForm - http://groups.google.com/group/django-developers/browse_thread/thread/2c33621003992d07/61a9fefb50d662b0 == Django tickets == There is a lot of discussion in these tickets as well, which are also superseded by this page. * #9977 (most up to date) * #10816 (remove session dependency from Django 1.0 middleware) == Types of attack == The following different attacks are in scope: 1. 'CSRF': normal CSRF attacks, where an attacking site hosts a form, link, or piece of javascript that causes the user's browser to make a request on a (Django) site. Normally this involves abuse of the user agent's authentication (e.g. a session/login cookie) to cause an action that the attacking site would not otherwise have permission to do. 2. 'Login CSRF': this is when a browser is tricked into logging into a site under an account that is controlled by an attacker, so that the attacker can then track or otherwise abuse the actions performed by the victim. 3. 'CSRF + MITM': this is a combination of CSRF and active network (man-in-the-middle) attack, which is relevant in HTTPS situations which are otherwise thought to be invulnerable to MITM attacks. This is because HTTP 'Set-Cookie' headers are accepted by clients that are talking to a site under HTTPS, allowing a MITM attacker to set cookies on the client. (MITM attacks under HTTP are considered out of scope, because in general MITM attacks under HTTP are impossible to protect against). Further, attacks can be categorised as: 1. Cross site - attacker.com attacking victim.com 2. Cross sub-domain - attacker.example.com attacking victim.example.com There is also an attack related to 'CSRF + MITM' which is not necessarily 'CSRF', which will be called 'session fixing': the opposite of session theft, a malicious agent sets the session cookie on a user's machine, giving them the attacker's session. The attacker may or may not be 'authenticated' in that session - if not, the victim might log in, and the attacker may be able to take control of the victim's account or abuse the victim's authentication. Alternatively, if the attacker is already authenticated, there are the same problems as those present in 'login CSRF'. This type of attack can be achieved by an active network attacker (not in scope for HTTP connections), but it can also be achieved by someone with control of a sub-domain through the use of the wild-card cookies. (Question: if the sub-domain is unable to send cookies, is this attack foiled - or do you need to stop javascript as well? The answer depends on browser policies about cookie setting, what happens when a page does {{{document.domain = 'example.com';}}} ?) == Methods of defence == The main contenders are: * 'HMAC of session identifier'. This was provided by Django 1.0 in !CsrfViewMiddleware, and is referred to as the 'CSRF token'. All incoming POST requests that have an active session are required to have a CSRF token that is a hash of the session identifier and the site's SECRET_KEY. * 'session independent nonce'. A random value is stored in a cookie, unique to every user, and POST forms must contain the same value as a token. * 'Strict Referer checking'. The HTTP 'REFERER' header must be present and must match the site's domain for the request to be accepted (for POST requests). == Methods of token insertion == * Django 1.0 provided !CsrfResponseMiddleware which did automatic insertion of the token in outgoing pages, to all POST forms. This has several problems: * The performance hit from doing the post processing. * It removes the ability to do streaming of responses (should that become a possibility in future Django versions) * It adds the CSRF token to all POST forms, including those targeted at external sites. These sites would then gain access to the CSRF token and would be able to do CSRF attacks on that user. (This can be avoided by use of the `@csrf_response_exempt` decorator if the page has no internal forms, but that might be an unacceptable constraint, and the default behaviour opens up vulnerabilities easily). To put it simply, control over token insertion is on a page by page basis, when it needs to be form by form. * !SafeForm - a Django Form subclass that adds the token. Proposal abandoned in favour of template tag for various reasons. * Template tag - templates that need the CSRF token need to insert them manually using `{% load csrf %}` at the top and `{% csrf_token %}` in every `