The Django documentation on its CSRF protection states that:
In addition, for HTTPS requests, strict referer checking is done by CsrfViewMiddleware. This is necessary to address a Man-In-The-Middle attack that is possible under HTTPS when using a session independent nonce, due to the fact that HTTP 'Set-Cookie' headers are (unfortunately) accepted by clients that are talking to a site under HTTPS. (Referer checking is not done for HTTP requests because the presence of the Referer header is not reliable enough under HTTP.)
I have trouble visualizing how this attack works. Could somebody explain?
UPDATE:
The wording in the Django doc seems to imply that there is a specific type of man-in-the-middle attack (which leads to a successful CSRF I'd assume) that works with session independent nonce (but not with transaction specific nonce etc., I suppose) and involves the use of 'Set-Cookie' header.
So I wanted to know how that specific type of attack works.
Directly from the django project
(I googled for session independent nonce.)
The Man-In-The-Middle attack explained in very simplistic terms. Imagine two people are talking to each other and before they start talking to each other, they do a handshake before they initiate a two way communication. When a third person starts to analyze how the two individuals how the two people communicate (What are their mannerisms?, Do they do a special handshake before they speak to each other?, What time do they like to talk to each other, etc), the third person can mold his/her communication to the point the he/she can embed themselves into a conversation and act as a mediator with the original two people thinking that they are speaking with each other.
Now take the concept and bring down to the geek level. When a pc, router, programs etc. communicates with another node unto the network, there is two-way communication occurs either by authentication, acknowledgement, or both. If a third party can determine the sequence of events that is required (session id, session cookie, the next sequence of acknowledge/transfer/termination in the traffic, etc), a malicious third party can mirror its own traffic as a legit node and flood the traffic to one of the legit nodes and if they get the right sequence of events down, the malicious third becomes accepted as a legit node.
Let's say we have a Django-powered site and a malicious Man-In-the-Middle. In the general case the site wouldn't even have to serve
http://
pages at all for the attack to succeed. In Django's case, it probably needs to serve at least one CSRF-protected page over plainhttp://
(see below for the explanation).The attacker first needs to get a syntactically-valid CSRF token. For some types of token (like a simple random string) she might be able to just make one up. For Django's scrambled tokens she will probably have to get one from an
http://
page that includes CSRF (e.g. in a hidden form field).The key point is that Django's CSRF tokens aren't tied to the user's session or any other saved state. Django will simply look to see if there is a match between the cookie and the form value (or header in the case of AJAX). So any valid token will do.
The user requests a page over
http://
. The attacker is free to modify the response since it's unencrypted. She does aSet-Cookie
with her malicious CSRF token, and alters the page to include a hidden form—and the Javascript to submit it—whichPOSTs
to anhttps://
endpoint. That form, of course, includes the field with the CSRF value.When the user's browser load the response, it stores the CSRF cookie specified by the
Set-Cookie
header and then runs the Javascript to submit the form. It sends thePOST
to thehttps://
endpoint along with the malicious CSRF cookie.(The "unfortunate" fact that cookies set over
http://
will be sent tohttps://
endpoints is discussed in the relevant RFC: "An active network attacker can also inject cookies into the Cookie header sent tohttps://example.com/
by impersonating a response fromhttp://example.com/
and injecting aSet-Cookie
header. The HTTPS server atexample.com
will be unable to distinguish these cookies from cookies that it set itself in an HTTPS response. An active network attacker might be able to leverage this ability to mount an attack againstexample.com
even ifexample.com
uses HTTPS exclusively.")Finally, the Django server receives the malicious
POST
request. It compares the CSRF cookie (set by the attacker) to the value in the form (set by the attacker) and sees that they are the same. It allows the malicious request.So, to avoid that result, Django also checks the
Referer
header (which is expected to always be set inhttps://
requests) against theHost
header. That check will fail in the example above because the attacker can't forge theReferer
header. The browser will set it to thehttp://
page that the attacker used to host her malicious form, and Django will detect the mismatch between that and thehttps://
endpoint that it's serving.Here's a very detailed description of one-such MitM attack. Below is an abridged and simplified adaptation:
Assume that:
Grab a valid CSRF token
MitM to force attacker-controlled POST to HTTPS page with that token:
Modify an HTTP-served page (e.g., http://foo.com/browse) to have an auto-submitting form that submits to an HTTPS POST end-point (e.g., http://foo.com/check_out). Also set their CSRF cookie to match your token: