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.