403 on POST for SSL nginx proxy

When things don't work as they should.
Post Reply
ironhacker
Posts: 3
Joined: Tue Jan 22, 2019 3:11 am

403 on POST for SSL nginx proxy

Post by ironhacker »

I have a direct install, version 3.1.9 on Ubuntu 18.04 LTS. I set up a proxy using nginx and all works well with http, but when I use https, I get a "forbidden" message embedded in the response for any POST. Chrome developer tools shows a 403 related to jquery, specifically /sources/create/from/local/multiple/. POST works fine under proxied http. The issue only arises under https. Here are my proxy defs for nginx:

Code: Select all

        location @proxy_to_app {
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header X-Forwarded-Proto $scheme;
                proxy_set_header Host $http_host;
                proxy_set_header X-Scheme $scheme;
                proxy_set_header X-SSL-Protocol $ssl_protocol;
                # we don't want nginx trying to do something clever with
                # redirects, we set the Host: header above already.
                proxy_redirect off;
                proxy_buffering off;
                proxy_pass http://app_server;
        }
There are no issues with jquery GET requests issued using SSL. Only POSTS. Any help is welcome. Thanks.


Update: 2019-01-22:

I turned on debugging and captured this error:

Code: Select all

Forbidden (Referer checking failed - Referer is malformed.): /sources/create/from/local/multiple/
This seems to be related to CSRF checks by Django that only happen when using HTTPS. No solution yet.

I believe these are the lines throwing errors in django/middleware/csrf.py:

Code: Select all

                # Make sure we have a valid URL for Referer.
                if '' in (referer.scheme, referer.netloc):
                    return self._reject(request, REASON_MALFORMED_REFERER)
scheme should be "https". Don't know about netloc, but I'm guessing it's /sources/create/from/local/multiple/

Update:

I have a solution. The csrf.py middleware reads a variable request.META.get('HTTP_REFERER'). While my browser, Chrome, says it's sending a Referer header with value https://mydomain, Django is reading the value "/sources/create/from/local/multiple/", which Chrome reports sending as the header X-Alt-Referer. Therefore, I added this line to the nginx proxy:

Code: Select all

 proxy_set_header X-Alt-Referer "https://mydomain";
I don't know if this is the proper way to handle this, but it's working for me.

proto
Posts: 1
Joined: Thu Mar 07, 2019 2:29 pm

Re: 403 on POST for SSL nginx proxy

Post by proto »

Hi,
this is my working setup on a FreeBSD jail:

nginx:

Code: Select all

upstream TESTME {
		server 127.0.0.1:9971;
}

server {
		server_name TESTME.DOMAIN.TLD;
		return 301 https://TESTME.DOMAIN.TLD$request_uri;
}

server {
		listen 443 ssl;
		server_name TESTME.DOMAIN.TLD;

		ssl_certificate /usr/local/etc/nginx/ssl/TESTME.crt;
		ssl_certificate_key /usr/local/etc/nginx/ssl/TESTME.key;

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

		proxy_set_header        Host $host;
		proxy_set_header        X-Real-IP $remote_addr;
		proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
		proxy_set_header        X-Forwarded-Proto $scheme;
		proxy_set_header        X-Forwarded-Host $host:$server_port;
		proxy_set_header        X-Forwarded-Server $host;

		location / {
				proxy_redirect off;
				proxy_pass http://TESTME;
		}

		location /static/ {
    		    root /home/ale/edms/media/static;
				proxy_pass http://TESTME;
		}

}

ironhacker
Posts: 3
Joined: Tue Jan 22, 2019 3:11 am

Re: 403 on POST for SSL nginx proxy

Post by ironhacker »

Found the culprit. It's this bit of code in middleware/ajax_redirect.py

Code: Select all

    def process_request(self, request):
        ajax_referer = request.META.get('HTTP_X_ALT_REFERER')

        if ajax_referer:
            request.META['HTTP_REFERER'] = ajax_referer
This sets HTTP_REFERER to:

Code: Select all

X-Alt-Referer: /sources/create/from/local/multiple/
The django CSRF middleware then uses the HTTP_REFERER and checks for a scheme and netloc, which then fails validation.

Code: Select all

                referer = force_text(
                    request.META.get('HTTP_REFERER'),
                    strings_only=True,
                    errors='replace'
                )
                if referer is None:
                    return self._reject(request, REASON_NO_REFERER)

                referer = urlparse(referer)

                # Make sure we have a valid URL for Referer.
                if '' in (referer.scheme, referer.netloc):
                    return self._reject(request, REASON_MALFORMED_REFERER)
Am I missing something? Looks like a bug to me.

Update:

So it looks like this is an unresolved bug: https://gitlab.com/mayan-edms/mayan-edms/issues/500 The temporary fix is what I posted originally (for nginx proxy). I'm now using nginx with uwsgi socket, and I need to add

Code: Select all

uwsgi_param HTTP_X_ALT_REFERER "https://host.domain";
to get it to work.

User avatar
rosarior
Developer
Developer
Posts: 505
Joined: Tue Aug 21, 2018 3:28 am
Location: Puerto Rico
Contact:

Re: 403 on POST for SSL nginx proxy

Post by rosarior »

Thanks! We are just now starting to understand the issue with the help of all that have participated. The next decision we need to take is if this will be fixed in Python at the middleware level (doing so clashes with some Django security middlewares) or at the JavaScript level in the partialNavigation.js module.

rvnovaes
Posts: 3
Joined: Fri Dec 13, 2019 8:47 pm
Location: Belo Horizonte/MG Brazil
Contact:

Re: 403 on POST for SSL nginx proxy

Post by rvnovaes »

Dear Friends,

I had the same issue using nginx as proxy on a direct installation. I am also using certbot and Lets Encrypt.

I rewrote the x-alt-referer using a chrome extension to be the https://SERVER_URL and it worked.

I have used a similar solution/workaround posted by former participants on the server block of nginx:

Code: Select all

proxy_set_header        X-Alt-Referer "";
And it has worked.

Post Reply