On 31 Aug 2015, at 12:35, Carl Meyer wrote:

I'm not sure what you mean by "unsafe but specifically cross-origin
request" here. I think the point is that the request is in fact safe,
because it's coming from an approved CORS source, but there's no way to
tell the CSRF middleware that.

Yes, exactly. However I avoided using the term CORS because (1) it is not supported in Django core and (2) specifically has use around `OPTIONS` requests and ensuring preflight browser requests succeed.

I've had this same problem in a CORS-accessible API scenario, and I've
used the same solution. Ideally the middleware should verify that the
request is a valid CORS request, from an approved host, before patching
the referer.

Exactly except the referrer should not be patched. The referrer may be used upstream in other code, what should change is the CSRF check.

Note that the most popular CORS-headers package for Django that I'm
aware of (https://github.com/ottoyiu/django-cors-headers) even includes
a middleware to patch the referer to get around this problem:
https://github.com/ottoyiu/django-cors-headers/blob/master/corsheaders/middleware.py#L26

I'm using that one at present, I did not know the middleware had that. Thanks!

   Suggested Solution

Allow specific referrer hosts to be accepted via the settings. The
change could be as simple as:

|good_referers = [ "https://%s/"; % host for host in
settings.ALLOWED_HOSTS ] if not any([same_origin(referer, good_referer)
for good_referer in good_referers]): ... |

I don't think just piggybacking on `ALLOWED_HOSTS` like this is
sufficient (though it would be a small improvement on the current
situation). `ALLOWED_HOSTS` is meant to be a list of host names that are
aliases for the _current_ server. It should not also include remote
servers that are authorized for CORS requests to this server. So in your
example, `dashboard.project.com` should _not_ be included in
`ALLOWED_HOSTS` on `api.project.com`, so this fix wouldn't help.

I used `ALLOWED_HOSTS` as a placeholder specifically because the connotation of allowing certain `Host` headers has a parallel with the `Referer` header. I think `ALLOWED_REFERERS` or similar, but definitely a separate setting, is needed.

Alternatively introduce a new setting that bypasses this secure referer
check.

I don't think there should be a setting to disable referer checking
altogether, but I do think there should be a way to make CORS and the
referer check work together without having to monkey-patch the request
referer.

A setting that allows bypassing and off by default would be the easiest way to keep HTTP vs. HTTPS CSRF checks identical.

The simplest approach would be a setting which is a list of valid CSRF
referer hosts. The problem with this is that it doesn't imply any
validation of the request CORS headers, but maybe that's OK. If you're
declaring "I trust this particular remote host not to CSRF my users,"
that's probably sufficient.

Agreed with simplest approach. What additional validation on the referer host would be added? You wouldn't add it if you didn't control it, at least that's my mindset. If I control `dashboard.project.com` is the case.

The argument could be made that this widens the attack surface, if you accept `Referer` requests from a large number of hosts, if one of those hosts is compromised that could be a point of entry. It's all tradeoffs, specifically in this case I have a separate frontend from the Django API and I'm very close to turning CSRF off entirely for lack of proper configuration.

Alternatives might include just adding CORS support to Django directly,
or providing a more generic hook for third-party CORS packages to take
over the referer check themselves.

I haven't thought about this, so I don't have a strong idea. I think the CORS is specific enough to utilize a separate system, my bigger irk here is that HTTP vs. HTTPS processing for CSRF is different and not configurable. It speaks strongly to Django's security mindset which I appreciate, but patching a header to allow requests to work doesn't make me feel good.

--jk

***
[me](http://kehn.us) | [@joshkehn](https://twitter.com/joshkehn)

--
You received this message because you are subscribed to the Google Groups "Django 
developers  (Contributions to Django itself)" 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.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-developers/77C7EBD8-220C-408D-9C2A-54F2F2E916E5%40gmail.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to