Django: CSRF Verification always fails

85 views Asked by At

So I have separate frontend and backend servers and when I'm performing any POST action from my frontend I'm passing a "Cookie" header with csrftoken inside, but for some reason it's not working. Then I've tried to pass in the csrftoken alone in the header, but it's still the same error. The strange part is that I can't see it in django request.META or request.headers. I use nginx for a reverse proxy and I use TLS for encryption.

This is the strange part, where is the header at? X_CSRFTOKEN and Cookie is being passed from my frontend

Django and django-cors config (And yeah I have all the required middlewares and in the right order):

NOTE: When http://dev.admin.lt is removed I get another error:

(Origin checking failed - http://dev.admin.lt does not match any trusted origins.): /auth/login/

CORS_ALLOWED_ORIGINS = [
    "https://127.0.0.1",
    "https://admin.lt",
    "https://dev.admin.lt",
    "https://api.admin.lt",
    "http://dev.admin.lt", # NOTE, without this we get another error
]


CSRF_TRUSTED_ORIGINS = [
    "https://127.0.0.1",
    "https://admin.lt",
    "https://dev.admin.lt",
    "https://api.admin.lt",
    "http://dev.admin.lt" # NOTE, without this we get another error
]


CORS_ALLOW_CREDENTIALS = True

CSRF_COOKIE_SECURE = True
CSRF_COOKIE_HTTPONLY = True
CSRF_COOKIE_SAMESITE = None
CSRF_COOKIE_DOMAIN = ".admin.lt"

# Is this actually doing anything?
SESSION_COOKIE_SECURE = True
SESSION_COOKIE_HTTPONLY = True
SESSION_COOKIE_SAMESITE = None
SESSION_COOKIE_DOMAIN = ".admin.lt"

request:

ipdb> request.headers
{'Content-Length': '', 'Content-Type': 'text/plain', 'Connection': 'upgrade', 'Host': 'api.domain.lt', 'X-Forwarded-For': '123.123.123.202', 'Cookie': 'csrftoken=caOdoVlJeaAxUzXLtQvjhgEXunWX5K5V; sessionid=jajl1faqtfkmsk9q8ykbjge6obk14wwh', 'Origin': 'http://dev.domain.lt', 'Accept': '*/*', 'Accept-Language': '*', 'Sec-Fetch-Mode': 'cors', 'User-Agent': 'node', 'Accept-Encoding': 'br, gzip, deflate'}

This is mine nginx config:

server
{
    server_name api.domain.lt;

    client_max_body_size 500m;

    #auth_basic "Restricted Content";
    #auth_basic_user_file /etc/nginx/.htpasswd;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection 'upgrade';
    proxy_set_header Host $host;
    proxy_cache_bypass $http_upgrade;
    proxy_set_header X-Forwarded-For $remote_addr;

    location /
    {

        proxy_pass http://localhost:8000;
    }

    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/api.domain.lt/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/api.domain.lt/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

}
1

There are 1 answers

0
Džiugas Bižokas On

None of the above solutions about ALLOWED_HOST or ORIGINS was the answer. The thing was that when submitting any form (POST) you need to pass in the cookies AND the header in order for it to work.

So its a standard security practice known as the "Double Submit Cookie" technique. This technique helps mitigate Cross-Site Request Forgery (CSRF) attacks.