Hi Josh,

On 08/31/2015 09:38 AM, Joshua Kehn wrote:
> I understand why the Referrer check for secure requests is in place.
> What is currently preventing cross-domain API requests is that the check
> is not configurable. I'm talking specifically about requests when
> |request.is_secure()| returns |True| and an unsafe but specifically
> cross-origin request is being made.

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.

> What currently exists is a simple referrer check.
> 
> |good_referer = 'https://%s/' % request.get_host() if not
> same_origin(referer, good_referer): reason = REASON_BAD_REFERER %
> (referer, good_referer) return self._reject(request, reason) |
[snip detailed example]
>  This check is not configurable and does
> not allow any hooks to bypass or alter behavior short of turning CSRF
> protection off entirely. This check is not present on non-secure
> requests and can cause surprise and confusion as to this new behavior
> when secure requests are enabled.
> 
> 
>       What are some possible solutions?
[snip unfeasible solutions]
>     What are you doing now to solve this issue?
> 
> Added a small piece of middleware the changes the |Referer| header to a
> "|good_referer|".

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.

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

>       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.

> 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.

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.

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.

Carl

-- 
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/55E4824B.4090802%40oddbird.net.
For more options, visit https://groups.google.com/d/optout.

Attachment: signature.asc
Description: OpenPGP digital signature

Reply via email to