#34855 closed Cleanup/optimization (wontfix)
Document CSRF_TRUSTED_ORIGINS relation to SECURE_PROXY_SSL_HEADER.
Reported by: | jeroenmuller | Owned by: | nobody |
---|---|---|---|
Component: | Documentation | Version: | 4.2 |
Severity: | Normal | Keywords: | CSRF |
Cc: | Florian Apolloner | Triage Stage: | Unreviewed |
Has patch: | no | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
When the CSRF origin check fails, the documentation points in the direction of adding that origin to CSRF_ALLOWED_ORIGINS. However, as far as I understand this should only be neccessary if there are actually cross-origin requests. In practice the CSRF origin check may also fail when is proxying HTTPS traffic over HTTP without setting up SECURE_PROXY_SSL_HEADER accordingly. This problem may occur suddenly when upgrading to Django 4.0, because this version turns on the origin verification in the CSRF middleware. In that case the Django release notes point in the direction of adding the failing origin to CSRF_ALLOWED_ORIGINS: https://docs.djangoproject.com/en/4.2/releases/4.0/#csrf-trusted-origins-changes-4-0. But this is not the optimal solution, and may weaken CSRF protection if a user ends up just adding a pattern matching all subdomains or the complete ALLOWED_HOSTS lists to CSRF_ALLOWED_ORIGINS.
It might be useful to add a note or warning below https://docs.djangoproject.com/en/4.2/ref/settings/#std-setting-CSRF_TRUSTED_ORIGINS explaining that it should only be neccessary to configure this if you are actually making requests across subdomains, and in other cases setting up SECURE_PROXY_SSL_HEADER might be a more appropriate solution (as long as the proxy correctly sets a header like X-Forwarded-Proto).
I ran into this problem when I updated a project hosted behind AWS ELB load balancer and gunicorn to Django 4.0, and the CSRF origin checking for the Django admin stopped working in a staging environment, with the message CSRF verification failed. Request aborted.
. I could not figure out what was going on using the documentation and ended up finding the root cause through a stackoverflow answer which pointed out that the origin header is compared against a string constructed of the host, prefixed by either http or https based on the result of request.is_secure()
In my case, the proxy setup was forwarding requests over HTTP, so is_secure() was false and CSRF was comparing the origin https://subdomain.my-domain.com
against "host" http://subdomain.my-domain.com
. It seems like the best solution here is to use SECURE_PROXY_SSL_HEADER to allow Django to determine if the client is using HTTPS or not, and use the "host" string https://subdomain.my-domain.com
. Looking at the referenced stackoverflow thread, this is a recurring problem for users updating to Django 4.0 and many end up with the sub-optimal solution of adding the origin with https to CSRF_ALLOWED_ORIGINS explicitly.
Change History (3)
comment:1 by , 14 months ago
Cc: | added |
---|---|
Easy pickings: | unset |
Resolution: | → wontfix |
Status: | new → closed |
Summary: | Documenting CSRF_TRUSTED_ORIGINS relation to SECURE_PROXY_SSL_HEADER could prevent misconfiguration when proxying HTTPs over HTTP → Document CSRF_TRUSTED_ORIGINS relation to SECURE_PROXY_SSL_HEADER. |
comment:2 by , 14 months ago
I would be okay with adding documentation for this if it is a recurring issue as long as it does not explicitly refer to SECURE_PROXY_SSL_HEADER
but is more like something along the lines of:
If you are seeing CSRF failures on HTTPS sites, it might be possible that your webserver/loadbalancer does not pass on the information that the site is exposed via HTTPS. Please consult the documentation of your webserver/loadbalancer on how to properly configure your site for HTTPS.
We could add "(this might include configuring SECURE_PROXY_SSL_HEADER
)" at the end.
comment:3 by , 8 weeks ago
When the CSRF origin check fails, the documentation points in the direction of adding that origin to CSRF_ALLOWED_ORIGINS. However, as far as I understand this should only be neccessary if there are actually cross-origin requests.
I can second this:
The documentation recommends this, as does "the internet" (top-voted answers on Stack Overflow), and pretty much everyone and their dog's blog. Rarely is there any mention that setting this should in fact only be required when you're doing anything cross-origin.
Using SECURE_PROXY_SSL_HEADER must be an informed decision as it may cause security issues.
Yes. But: adding random stuff to CSRF_ALLOWED_ORIGINS
should also be an informed decision.
I found it useful to instead push to understanding the problem before proceeding, by getting more verbose error messages from your middleware. Not the entire answer, but yet another puzzle piece.
I'm not pushi
Replying to jeroenmuller:
Using
SECURE_PROXY_SSL_HEADER
must be an informed decision as it may cause security issues. This is not something we would freely document as a default solution, especially, since it's only a solution for this particular setup where you have a trusted proxy that changes the protocol. I'm skeptical, we cannot document all setting configurations.