TandoorRecipes / recipes

Application for managing recipes, planning meals, building shopping lists and much much more!
https://docs.tandoor.dev
Other
5.56k stars 589 forks source link

CSRF failure through reverse proxy even with Required Headers #2555

Closed axeleroy closed 1 year ago

axeleroy commented 1 year ago

Issue

Hello,

I set up Tandoor using the Docker "plain" setup with a reverse proxy in front of it (BunkerWeb) but unfortunately some requests made by the front-end fail with the following error: CSRF Failed: CSRF token missing.

I then followed the docs and updated my reverse proxy configuration in order to set the Host and X-Forwarded-Proto headers but the issue persisted. I almost went crazy thinking my reverse proxy would not follow my custom configuration or nuke the csrftoken cookie but I ended modifying Tandoor's nginx configuration to log the request headers, and lo and behold everything is there :exploding_head:

{
   "Host":"recipes.selfhosting.axeleroy.com",
   "X-Forwarded-For":"192.168.0.1",
   "X-Real-IP":"192.168.0.1",
   "X-Forwarded-Proto":"https",
   "X-Forwarded-Protocol":"https",
   "X-Forwarded-Host":"recipes.selfhosting.axeleroy.com",
   "Connection":"close",
   "Content-Length":"75",
   "user-agent":"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0",
   "accept":"application/json, text/plain, */*",
   "accept-language":"fr-FR,en-US;q=0.7,en;q=0.3",
   "accept-encoding":"gzip, deflate, br",
   "content-type":"application/json",
   "referer":"https://recipes.selfhosting.axeleroy.com/data/import/url",
   "origin":"https://recipes.selfhosting.axeleroy.com",
   "dnt":"1",
   "sec-fetch-dest":"empty",
   "sec-fetch-mode":"cors",
   "sec-fetch-site":"same-origin",
   "sec-gpc":"1",
   "pragma":"no-cache",
   "cache-control":"no-cache",
   "cookie":"rl_session=_redacted_; rl_page_init_referring_domain=_redacted_; rl_user_id=_redacted_; rl_trait=_redacted_; ph_phc_4URIAm1uYfJO7j8kWSe0J8lc8IqnstRLS7Jx8NcakHo_posthog=_redacted_; csrftoken=N9jUOeaAcJ9NYg0HbvdBKMEg93vZdfcR; sessionid=3tczuut2s745z2vxmnpuc8hw6i2xpqca"
}

And frustratingly enough, when accessing the front-end directly through Tandoor's nginx the request works as expecte...

So I'm left scratching my head, I don't know what I could have missed or where I should look :/ Thanks in advance for your help!

Tandoor Version

1.5.4

OS Version

TrueNAS SCALE 22.12.0

Setup

Docker / Docker-Compose

Reverse Proxy

Others (please state below)

Other

BunkerWeb 1.4.0 (nginx-based)

Environment file

DEBUG=0
SQL_DEBUG=0
DEBUG_TOOLBAR=0
ALLOWED_HOSTS=*
SECRET_KEY=_redacted_
TIMEZONE=Europe/Paris
DB_ENGINE=django.db.backends.postgresql
POSTGRES_HOST=db_recipes
POSTGRES_PORT=5432
POSTGRES_USER=djangouser
POSTGRES_PASSWORD=_redacted_
POSTGRES_DB=djangodb
FRACTION_PREF_DEFAULT=0
COMMENT_PREF_DEFAULT=1
SHOPPING_MIN_AUTOSYNC_INTERVAL=5
GUNICORN_MEDIA=0
REVERSE_PROXY_AUTH=0

Docker-Compose file

version: "3"
services:
  db_recipes:
    restart: always
    image: postgres:15-alpine
    volumes:
      - postgresql:/var/lib/postgresql/data
    env_file:
      - stack.env
    networks:
      - tandoor-net

  web_recipes:
    restart: always
    image: vabene1111/recipes:1.5.4
    env_file:
      - stack.env
    volumes:
      - staticfiles:/opt/recipes/staticfiles
      - nginx_config:/opt/recipes/nginx/conf.d
      - mediafiles:/opt/recipes/mediafiles
    depends_on:
      - db_recipes
    networks:
      - tandoor-net

  nginx_recipes:
    image: nginx:mainline-alpine
    restart: always
    env_file:
      - stack.env
    depends_on:
      - web_recipes
    volumes:
      - nginx_config:/etc/nginx/conf.d:ro
      - staticfiles:/static:ro
      - mediafiles:/media:ro
    networks:
      - tandoor-net
      - bunkerweb-net

volumes:
  nginx_config: {}
  staticfiles: {}
  postgresql: {}
  mediafiles: {}

networks: 
  tandoor-net:
    name: tandoor-net
  bunkerweb-net:
    name: bunkerweb-net
    external: true

Relevant logs

No response

smilerz commented 1 year ago

I don't know reverse proxies well enough to decode what you have is interferring - my suggestion is to get your reverse down to minimum config and add back the additional configurations to determine what is breaking.

My instincts are something in one of these lines could be doing something unexpected. Content-length of 75 looks

"referer":"https://recipes.selfhosting.axeleroy.com/data/import/url",
"sec-fetch-dest":"empty",
"sec-fetch-mode":"cors",
"sec-fetch-site":"same-origin",
"sec-gpc":"1",
"cookie":"xxxxxx"
axeleroy commented 1 year ago

Well, the thing is BunkerWeb does hardening and adds security headers by default, its configuration is already close to its default (which I admit is far from vanilla nginx) :/

smilerz commented 1 year ago

Well, the thing is BunkerWeb does hardening and adds security headers by default, its configuration is already close to its default (which I admit is far from vanilla nginx) :/

hard to fix it until you can figure out which part is breaking it....

this looks relevant - does your env include CSRF_TRUSTED_ORIGINS?

axeleroy commented 1 year ago

this looks relevant - does your env include CSRF_TRUSTED_ORIGINS ?

It did not include it, but after adding the variable it hasn't changed anything.

linuxturtle commented 1 year ago

I'm having the same issue through haproxy. I can sorta half-solve it by adding CSRF_TRUSTED_ORIGINS="https://my.trusted.domain" to my .env. That gets me through the login screen, but nothing really works once I'm in, and I just get "Failure: There was an error fetching a resource!" errors whenever I click on anything in the web UI.

axeleroy commented 1 year ago

Hello,

I looked into the issue once again by comparing requests made directly to Tandoor and through the proxy and I've found that the X-CSRF-TOKEN header is missing.

After a brief search in BunkerWeb's issue tracker, I've found the culprit and a fix: BunkerWeb adds the HttpOnly flag to cookies, preventing Javascript from accessing them and thus the front-end to build and add the X-CSRF-TOKEN!

Changing the configuration to remove the HttpOnly flag fixed the issue.

wrjlewis commented 1 year ago

HttpOnly

Just wanted to thank you for documenting this here.. I was going crazy until I stumbled upon this solution, which worked for me too. I had previously hardened my vanilla apache setup with HttpOnly, so removing that and logging out & in again worked for me.