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.
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)
```
Let's discuss two domains, `api.project.com` and
`dashboard.project.com`. Both are served over HTTPS. Both are fronted
with a reverse proxy that performs TLS termination.
Inside the Django settings normally one would configure:
```python
# Read the special header set by the reverse proxy that indicates a
secure request
SECURE_PROXY_SSL_HEADER = ("HTTP_FORWARDED", "https")
# Set the cookies on the root domain, allows api-dev.project.com to be
configured
CSRF_COOKIE_DOMAIN = ".project.com"
SESSION_COOKIE_DOMAIN = ".project.com"
# Configure hosts we allow
ALLOWED_HOSTS = [
"api.project.com"
]
```
Now a `POST` request comes in to the API from a user on
`dashboard.project.com/posts/new`. Django sees the following:
* `POST not in ('GET', 'HEAD', 'OPTIONS', 'TRACE')`, this is an
"unsafe" request
* `request.is_secure()` is `True`
* `Host` header is `api.project.com`
* Referrer is `dashboard.project.com/posts/new`
At this point, the `same_origin(referer, good_referer)` check fails on
[middleware/csrf.py:159](https://github.com/django/django/blob/8047e3666b0b50bb04e6f16c2a4fb21ddfd5713f/django/middleware/csrf.py#L159)
and the entire request breaks. 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?
Why not configure the Host header when sending the request?
Causes all absolute reversed URLs to use that Host header which is
obviously incorrect.
What about serving behind the same reverse proxy and provide path
based routing?
Path based routing is a separate discussion but it introduces cache
problems and configuration problems. Nevertheless, Django should not be
required to serve on the same `Host` as the originator with zero
configuration.
Why not write some custom middleware that does your own CSRF checks?
Not DRY, complicated, prone to failure. Would prefer to not roll my own
security code for obvious reasons.
What are you doing now to solve this issue?
Added a small piece of middleware the changes the `Referer` header to a
"`good_referer`".
### 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]):
...
```
Alternatively introduce a new setting that bypasses this secure referer
check. I've outlined the common scenario where this check fails to
provide any usefulness. While I believe this check provides additional
security under ideal and simple use cases I don't believe Django's
security should be relegated to the simple case.
I'd greatly welcome any comments or feedback on this matter, including
alternative suggestions to resolving the issue that don't require making
changes in Django core.
Best,
-Josh
***
[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/F09FAEDA-3FF6-4339-9CC4-EF89649F31A5%40gmail.com.
For more options, visit https://groups.google.com/d/optout.