authentik icon indicating copy to clipboard operation
authentik copied to clipboard

CSRF does not work as expected

Open poelzi opened this issue 1 year ago • 4 comments

Describe the bug Trying to create a provider backend on a test system fails due

{"detail":"CSRF Failed: Origin checking failed - https://login.gaggalacka.local:4443 does not match any trusted origins."}

Request to create provider

Request URL: https://login.gaggalacka.local:4443/api/v3/providers/oauth2/
Request Method: POST
Status Code: 403 
Remote Address: 127.0.0.1:4443
Referrer Policy: same-origin
allow: GET, POST, HEAD, OPTIONS
content-encoding: gzip
content-length: 129
content-type: application/json
date: Wed, 15 Jun 2022 00:27:11 GMT
referrer-policy: same-origin
server: nginx
vary: Accept-Encoding
vary: Cookie
x-authentik-id: ec7233df1a33482f90f2af0ef00bb410
x-content-type-options: nosniff
x-frame-options: DENY
x-powered-by: authentik
:authority: login.gaggalacka.local:4443
:method: POST
:path: /api/v3/providers/oauth2/
:scheme: https
accept: */*
accept-encoding: gzip, deflate, br
accept-language: en-US,en;q=0.9,de;q=0.8
cache-control: no-cache
content-length: 718
content-type: application/json
cookie: authentik_csrf=VjOki6JmlN7LARoWwgmktUA8rHblsMBG9y7NR0RdDMQTSZYA6wpRaBuyg8AqiyGY; authentik_session=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzaWQiOiJvN2xsaDE4ZWpnM3ZkZjRzeW5qcG5mcWcydzJjaGszYiIsImlzcyI6ImF1dGhlbnRpayIsInN1YiI6IjMwZDE2YTIxYWE2MDM0NDNiOGMzMzljYzgxMjlhY2NiYjU0MzY0M2FmNzYzMzUwY2U1NWNhNTNlNDAyMzliZDciLCJhdXRoZW50aWNhdGVkIjp0cnVlLCJhY3IiOiJnb2F1dGhlbnRpay5pby9jb3JlL2RlZmF1bHQifQ.qAcNSkgJAy8EbB6rFwptttNTwvyOOh70kh7-JgAf_9I
origin: https://login.gaggalacka.local:4443
pragma: no-cache
referer: https://login.gaggalacka.local:4443/if/admin/
sec-ch-ua: "Chromium";v="97", " Not;A Brand";v="99"
sec-ch-ua-mobile: ?1
sec-ch-ua-platform: "Android"
sec-fetch-dest: empty
sec-fetch-mode: cors
sec-fetch-site: same-origin
sentry-trace: fca9059d533e49c484e129919eeece54-92545824b0a75666-0
user-agent: Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Mobile Safari/537.36
x-authentik-csrf: VjOki6JmlN7LARoWwgmktUA8rHblsMBG9y7NR0RdDMQTSZYA6wpRaBuyg8AqiyGY

docker exec -it authentik-worker ak dump_config

{"event": "Loaded config", "level": "debug", "logger": "__main__", "timestamp": 1655253485.2783082, "file": "/authentik/lib/default.yml"}
{"event": "Loaded environment variables", "level": "debug", "logger": "__main__", "timestamp": 1655253485.2804134, "count": 9}
{
    "postgresql": {
        "host": "172.17.0.1",
        "name": "authentik",
        "user": "authentik",
        "port": 5432,
        "password": "uiae"
    },
    "web": {
        "listen": "0.0.0.0:9000",
        "listen_tls": "0.0.0.0:9443",
        "listen_metrics": "0.0.0.0:9300",
        "outpost_port_offset": 0
    },
    "redis": {
        "host": "172.17.0.1",
        "port": 6379,
        "password": "",
        "tls": false,
        "tls_reqs": "none",
        "cache_db": 0,
        "message_queue_db": 1,
        "ws_db": 2,
        "outpost_session_db": 3,
        "cache_timeout": 300,
        "cache_timeout_flows": 300,
        "cache_timeout_policies": 300,
        "cache_timeout_reputation": 300
    },
    "debug": false,
    "log_level": "trace",
    "error_reporting": {
        "enabled": true,
        "environment": "customer",
        "send_pii": false,
        "sample_rate": 0.3
    },
    "email": {
        "host": "localhost",
        "port": 25,
        "username": "",
        "password": "",
        "use_tls": false,
        "use_ssl": false,
        "timeout": 10,
        "from": "authentik@localhost"
    },
    "outposts": {
        "container_image_base": "ghcr.io/goauthentik/%(type)s:%(version)s",
        "discover": true
    },
    "cookie_domain": null,
    "disable_update_check": false,
    "disable_startup_analytics": false,
    "avatars": "gravatar",
    "geoip": "/geoip/GeoLite2-City.mmdb",
    "footer_links": [],
    "default_user_change_name": true,
    "default_user_change_email": true,
    "default_user_change_username": true,
    "gdpr_compliance": true,
    "cert_discovery_dir": "/certs",
    "default_token_length": 128,
    "impersonation": true,
    "csrf_trusted_origins": "['login.gaggalacka.local:4443','login.gaggalacka.local','https://login.gaggalacka.local:4443','https://login.gaggalacka.local',]",
    "secret_key": "uiae"
}

Every possible form is listed correctly in "csrf_trusted_origins"

To Reproduce Steps to reproduce the behavior:

  1. Go to create provider
  2. Create oauth2 porvider

Expected behavior provider created

Logs Output of docker-compose logs or kubectl logs respectively

Version and Deployment (please complete the following information):

  • authentik version: 2022.6.2
  • Deployment: docker

poelzi avatar Jun 15 '22 00:06 poelzi

If I use ssh portforwarding into the vm, and access the worken not through nginx, it works.

My nginx config:

pid /run/nginx/nginx.pid;
error_log stderr;
daemon off;
events {
}
http {
	# The mime type definitions included with nginx are very incomplete, so
	# we use a list of mime types from the mailcap package, which is also
	# used by most other Linux distributions by default.
	include /nix/store/qd3g8rk5hx5zkb70idjh6fa12sh6bipg-mailcap-2.1.53/etc/nginx/mime.types;
	# When recommendedOptimisation is disabled nginx fails to start because the mailmap mime.types database
	# contains 1026 enries and the default is only 1024. Setting to a higher number to remove the need to
	# overwrite it because nginx does not allow duplicated settings.
	types_hash_max_size 4096;
	include /nix/store/hbj1d2pi7r09m29dq8d9gnn6zz749pcc-nginx-1.20.2/conf/fastcgi.conf;
	include /nix/store/hbj1d2pi7r09m29dq8d9gnn6zz749pcc-nginx-1.20.2/conf/uwsgi_params;
	default_type application/octet-stream;
	ssl_protocols TLSv1.2 TLSv1.3;
	ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
	# $connection_upgrade is used for websocket proxying
	map $http_upgrade $connection_upgrade {
		default upgrade;
		''      close;
	}
	client_max_body_size 10m;
	server_tokens off;
	server {
		listen 0.0.0.0:443 ssl http2 ;
		listen [::0]:443 ssl http2 ;
		listen 0.0.0.0:80 ;
		listen [::0]:80 ;
		server_name login.gaggalacka.local ;
		location /.well-known/acme-challenge {
			root /var/lib/acme/acme-challenge;
			auth_basic off;
		}
		ssl_certificate /var/lib/acme/login.gaggalacka.local/fullchain.pem;
		ssl_certificate_key /var/lib/acme/login.gaggalacka.local/key.pem;
		ssl_trusted_certificate /var/lib/acme/login.gaggalacka.local/chain.pem;
		location / {
			proxy_pass https://127.0.0.1:9443;
			proxy_http_version 1.1;
			proxy_set_header Upgrade $http_upgrade;
			proxy_set_header Connection $connection_upgrade;
			proxy_set_header X-Forwarded-Proto https;
			proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
			# This needs to be set inside the location block, very important.
			proxy_set_header Host $host;
		}
	}
}

This is what the doc pretty much suggests.

poelzi avatar Jun 15 '22 01:06 poelzi

Since the problem only seems to happen when accessing the test vm on non standard port, the live system with 443 seems unaffected, I think this is the culpid.

poelzi avatar Jun 17 '22 12:06 poelzi

The should be fixed by using the nginx config from the docs https://goauthentik.io/docs/installation/reverse-proxy

BeryJu avatar Jul 02 '22 15:07 BeryJu

Your requests do not match your nginx and authentik configs. You use default port 9443 in everything, but then your request shows 4443. Why is that? Is there another piece between nginx and your browser? Ensure that piece is properly passing headers. Showing the requests/responses via nginx or whatever that middle component is would also help understand the situation.

The nginx config looks good actually except you are using the default $connection_upgrade header, look at the link above to see how to configure it so that you get HTTP/1.1 keepalive functionality (i.e. ensure Connection: close header is not sent by adjusting the map to send the empty string instead of close).

sevmonster avatar Jul 08 '22 03:07 sevmonster

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Sep 06 '22 06:09 stale[bot]

If someone else has this issue - it helped me to manually add referer to nginx config, which handles connection to authentik:

proxy_set_header Referer "https://my-authentik-domain.com:443";

jehy avatar Jan 02 '24 02:01 jehy

That is a really bad not good idea and completely sidesteps CSRF protection. By forcing the Referer you open all of your users up to CSRF. The fact that the Origin header will not match could also cause issues.

If you provided your configurations we might be able to help you.

sevmonster avatar Jan 03 '24 00:01 sevmonster

That is a really bad not good idea and completely sidesteps CSRF protection. By forcing the Referer you open all of your users up to CSRF. The fact that the Origin header will not match could also cause issues.

If you provided your configurations we might be able to help you.

I have authentik behind the nginx. Simply because I want all requests to my web services pass through it. And it looks that by default nginx does not pass referer, so I have csrf error message. But I don't think setting referer manually is really bad since only requests to authentik host can pass through nginx. But may be I can pass original referer and it will work out. I will try.

UPD. Yup, adding proxy_set_header Referer $http_referer; worked for me.

jehy avatar Jan 03 '24 00:01 jehy

Nginx passes (almost) all request headers by default, including Referer. You only have to explicitly pass headers if you have disabled proxy_pass_request_headers.

But I don't think setting referer manually is really bad since only requests to authentik host can pass through nginx.

Bypassing CSRF like this is inherently dangerous as it opens a door that someone could use to exploit your users. CSRF isn't the worst possible risk vector since it can be mitigated by other factors, but it's still bad.

https://owasp.org/www-community/attacks/csrf

sevmonster avatar Jan 03 '24 13:01 sevmonster