Django CSRF Verification failed for admin panel
Asked Answered
M

3

9

I started a fresh Django 1.11 project with one app, one model and one admin panel. Locally, everything works. When I deploy it to Amazon EC2 and try to log in to the admin panel, I get a 403 (CSRF verification failed. Request aborted.). I see this in my debug log:

[WARNING] 2017-05-21 11:23:52,142 csrf 14263 140377210439424 Forbidden (Referer checking failed - Referer is insecure while host is secure.): /admin/login/

I inspected with Chrome's network utility the request, and I noticed that in my Request Header I have:

Cookie:csrftoken=hFhzOJPMOhkNWWWfRtlMOEum9jXV8XXWnOtw3OwZm2En9JUqYRVq632xyZfwSpzU

while in my Form Data I have:

csrfmiddlewaretoken:RHNpPfOHhg42FZnXmn9PZgNm3bN40C41XQZm4kvUP1oCSMl8tLJthFlxsR5FK4GZ

Should these two be the same? In my understanding they do, but when I try the same in my local environment, I see they're also not the same, but there it is working fine and I get the same token back in the Response Header as was sent in the Request Header, so I assume they don't need to be exactly the same? Note: I do not have a secure connection (https) at the moment, but will work on that after this is fixed.

I already tried/checked the following:

Other answers I found on SO mention that you need to do something in the form itself, but this is a form from the Django framework.

Additional information

My nginx configuration from /etc/nginx/nginx.conf:

user www-data;
worker_processes auto;
pid /run/nginx.pid;

events {
    worker_connections 768;
    # multi_accept on;
}

http {

    ##
    # Basic Settings
    ##

    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;
    # server_tokens off;

    # server_names_hash_bucket_size 64;
    # server_name_in_redirect off;

    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    ##
    # SSL Settings
    ##

    ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
    ssl_prefer_server_ciphers on;

    ##
    # Logging Settings
    ##

    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;

    ##
    # Gzip Settings
    ##

    gzip on;
    gzip_disable "msie6";

    # gzip_vary on;
    # gzip_proxied any;
    # gzip_comp_level 6;
    # gzip_buffers 16 8k;
    # gzip_http_version 1.1;
    # gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

    ##
    # Virtual Host Configs
    ##

    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
}

My site specific configuration from /etc/nginx/sites-enabled/MyDjangoService:

upstream MyDjangoService_wsgi_server {
  # fail_timeout=0 means we always retry an upstream even if it failed
  # to return a good HTTP response (in case the Unicorn master nukes a
  # single worker for timing out).

  server unix:/webapps/MyDjangoService/run/gunicorn.sock fail_timeout=0;
}

server {
    listen      80;
    server_name MyDjangoService;

    client_max_body_size 4G;

    access_log /webapps/MyDjangoService/logs/nginx_access.log;
    error_log /webapps/MyDjangoService/logs/nginx_error.log;

    location /static/ {
        alias   /webapps/MyDjangoService/static/;
    }

    location /media/ {
        alias   /webapps/MyDjangoService/media/;
    }

    location / {
        if (-f /webapps/MyDjangoService/maintenance_on.html) {
            return 503;
        }

        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto https;
        proxy_set_header Host $http_host;
        proxy_redirect off;

        # Try to serve static files from nginx, no point in making an
        # *application* server like Unicorn/Rainbows! serve static files.
        if (!-f $request_filename) {
            proxy_pass http://MyDjangoService_wsgi_server;
            break;
        }
    }

    # Error pages
    error_page 500 502 504 /500.html;
    location = /500.html {
        root /webapps/MyDjangoService/django/src/MyDjangoService/templates/;
    }

    error_page 503 /maintenance_on.html;
    location = /maintenance_on.html {
        root /webapps/MyDjangoService/;
    }
}
Maleeny answered 21/5, 2017 at 10:54 Comment(8)
Django 1.10 and higher pads the token with a random string to protect against BREACH attacks, so they will not be the same even if they are correct. Django should give you a reason why the CSRF check failed, can you show the exact error? If DEBUG is off, Django should still log the exact error, so check your logs if you're not sure.Recent
Thanks. I have added the error from the debug log to the question. Forbidden (Referer checking failed - Referer is insecure while host is secure.Maleeny
It seems like Django incorrectly thinks it is running on https, but the referer url is using http. Can you show the relevant server configuration, and your SECURE_PROXY_SSL_HEADER setting?Recent
Which is the relevant server configuration? Where can I find it? I looked up the setting: SECURE_PROXY_SSL_HEADER = None.Maleeny
Most likely your Apache or Nginx configuration for this site.Recent
I have added the nginx.conf file to the question. Is this the configuration you mean?Maleeny
Can you show your site-specific configuration as well? It's probably in sites-enabled/.Recent
Let us continue this discussion in chat.Maleeny
R
6

Your issue is in the following line:

        proxy_set_header X-Forwarded-Proto https;

Here you unconditionally set the X-Forwarded-Proto header to the value https. Your WSGI server will interpret this to mean that your site is running behind https. Django then does a strict referrer check, and sees that the protocol in the referrer domain is http instead of https. Because this can be a security issue, Django rejects the request.

You should either remove this line, or change it to use the correct value. You can use the $scheme variable for this:

    proxy_set_header X-Forwarded-Proto $scheme;
Recent answered 21/5, 2017 at 15:2 Comment(0)
T
1

If for some reason you want or need to have https unconditionally forwarded and you are working in a localhost, get past the CSRF error on the admin page by putting

CSRF_TRUSTED_ORIGINS = ["127.0.0.1"]       
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')

in your settings.py file and using https://127.0.0.1:443/admin in your browser

Transcribe answered 7/7, 2020 at 18:14 Comment(0)
B
0

I had the same issue, my issue was that I was using a subdomain that was already in use. So I change that and nginx worked... The problem that you are experimenting has to do with http and https requests, apparently Django blocks the request using that type of config:

proxy_set_header X-Forwarded-Proto https;

I'll leave you my config... hope it helps:

upstream your_app_server {
    server unix:/srv/venv/run/gunicorn.sock fail_timeout=0;
}

server {
    listen 80;
    listen 443 ssl;

    ssl_certificate path_to_your_certificado.pem;
    ssl_certificate_key path_to_your_certificado.key;

    server_name testing.autopaquete.com.mx;
    client_max_body_size 4G;

    access_log /srv/venv/logs/nginx-access.log;
    error_log /srv/venv/logs/nginx-error.log;

    location /assets/ {
        alias /srv/venv/your_app/templates/;
    }

    location /media/ {
        alias /srv/venv/your_app/media/;
    }

    location /static_files/ {
        alias /srv/venv/your_app/static_files/;
    }

    location /static/  {
        alias /srv/venv/your_app/static/;
    }

    location /templates/ {
        alias /srv/venv/your_app/templates/;
    }

    location / {

    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        proxy_set_header Host $host;

        proxy_redirect off;

        if (!-f $request_filename) {
            proxy_pass http://your_app_server;
            break;
        }
    }

    # Error pages
    error_page 500 502 503 504 /500.html;

    location = /500.html {
        root /srv/venv/your_app/templates/;
    }

}
Breadstuff answered 25/3 at 4:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.