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 » Tue Jan 22, 2019 3:21 am

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 » Thu Mar 07, 2019 2:45 pm

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 » Tue Oct 22, 2019 5:14 pm

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
Posts: 406
Joined: Tue Aug 21, 2018 3:28 am

Re: 403 on POST for SSL nginx proxy

Post by rosarior » Mon Oct 28, 2019 12:39 am

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.

Post Reply