简体   繁体   中英

Django + Caddy = CSRF protection issues

I deployed a Django 4 app with Daphne (ASGI) in a docker container. I use Caddy as a reverse proxy in front. It works, except I can't fill in any form because the CSRF protection kicks in. So no admin login, for example.

I can currently access the admin interface in two ways:

  1. Directly through docker, via a SSH tunelled port
  2. Through Caddy, which is then forwarding to the Docker container.

Option 1 works. I can log into the admin interface just as if I was running the development server locally. All is working as expected.

However, option 2 (caddy reverse proxy) doesn't work. I can access Django and load pages, but any form submission will be blocked because the CSRF protection kicks in.

CSRF verification failed. Request aborted.

Reason given for failure:
    Origin checking failed - https://<mydomain.com> does not match any trusted origins.

My Caddyfile contains this:

<mydomain.com> {
       reverse_proxy localhost:8088
}

localhost:8088 is the port exposed by my docker container.

In an effort to eliminate potential issues, I've set the following to false in my config file:

  • SECURE_SSL_REDIRECT (causes a redirect loop, probably related to the reverse proxying)
  • SESSION_COOKIE_SECURE (I'd rather have it set to True, but I don't know at this point)
  • CSRF_COOKIE_SECURE (same remark)

The only Django-Caddy examples I could find online are outdated and refer to older versions of Caddy and/or Django. Django is deployed on ASGI with Daphne.

I've seens posts suggesting to change CSRF_TRUSTED_ORIGINS , but it doesn't seem right that I would have to add a host that is already in the ALLOWED_HOSTS list. That also wouldn't explain why it works directly on the docker container, unless localhost is a special case for CSRF.

Versions:

  • Caddy: 2.5.1
  • Django: 4.0.5
  • Daphne: 3.0.2
  • Python: 3.10.5

Any idea what goes wrong, and how I should go about debugging such issues?

Finally found out what was happening.

I first wanted to know the exact HTTP request that was sent from caddy to django:

sudo tcpdump -i lo -A -n port 8088

This confirmed that:

  • the Origin and Referer headers were set properly
  • the csrftoken cookie was sent properly

Once that was known, I could dig in the code from django. Specifically, this function in the CSRF middleware .

In conclusion:

  1. Caddy forwards the http request to django unencrypted (so HTTP-non-S between caddy and django).
  2. Django considers that request non-secure
  3. The CSRF protection expects the Origin header sent by the browser to be http:// because the request is not secure. In my case, it is https:// because my browser is talking to Caddy over https
  4. Because the Origin header does not match what the CSRF middleware expects, the request is rejected

It's actually a simple fix.

Since we know that Caddy will always ignore X-Forwarded-Proto from the browser and sets it itself, we can add SECURE_PROXY_SSL_HEADER to the settings.py in django:

SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')

And voilà!

Now I can also set these to true:

  • SECURE_SSL_REDIRECT
  • SESSION_COOKIE_SECURE
  • CSRF_COOKIE_SECURE

EDIT Here's the Caddyfile, as per requested:

service.mywebsite.com {
        reverse_proxy localhost:8088
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM