Issue
I have a Django website at http://example.com that works fine, including post requests. I've added HTTPS so my site is accessible at https://example.com too.
I can load any page on HTTPS, but I always get CSRF validation errors when I try to POST. POST requests work fine on HTTP.
My Django process is running with gunicorn behind nginx, and I have nginx setting X_Forwarded_For
. So, an HTTPS request has the following headers (taken from request.META
):
'HTTP_ACCEPT': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'HTTP_ACCEPT_ENCODING': 'gzip, deflate',
'HTTP_ACCEPT_LANGUAGE': 'en-US,en;q=0.5',
'HTTP_CONNECTION': 'close',
'HTTP_COOKIE': 'redacted',
'HTTP_HOST': 'example.com:80',
'HTTP_REFERER': 'https://example.com/user/delete/49/',
'HTTP_USER_AGENT': 'Mozilla/5.0 (X11; Linux x86_64; rv:24.0) Gecko/20100101 Firefox/24.0',
'HTTP_X_FORWARDED_FOR': '1.2.3.4, 192.168.252.22',
'HTTP_X_FORWARDED_PROTO': 'https',
'HTTP_X_REAL_IP': '1.2.3.4',
and an HTTP request has the following headers:
'HTTP_ACCEPT': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'HTTP_ACCEPT_ENCODING': 'gzip, deflate',
'HTTP_ACCEPT_LANGUAGE': 'en-US,en;q=0.5',
'HTTP_CONNECTION': 'close',
'HTTP_COOKIE': 'redacted',
'HTTP_HOST': 'example.com.com:80',
'HTTP_USER_AGENT': 'Mozilla/5.0 (X11; Linux x86_64; rv:24.0) Gecko/20100101 Firefox/24.0',
'HTTP_X_FORWARDED_FOR': '1.2.3.4, 192.168.252.22',
'HTTP_X_FORWARDED_PROTO': 'http',
'HTTP_X_REAL_IP': '1.2.3.4',
Why doesn't CSRF work on HTTPS when I have no problems over HTTP?
Solution
Turns out it was an nginx configuration issue. My server setup is:
nginx -> nginx -> gunicorn
On the second nginx system, I had
proxy_set_header Host $host:$server_port;
However, since HTTPS is terminated at the first nginx, $server_port
was always 80.
On HTTPS, Django does strict referer checking (see point 4). Looking at the Django source:
good_referer = 'https://%s/' % request.get_host()
if not same_origin(referer, good_referer):
reason = REASON_BAD_REFERER % (referer, good_referer)
logger.warning('Forbidden (%s): %s', reason, request.path,
extra={
'status_code': 403,
'request': request,
}
)
return self._reject(request, reason)
CSRF validation was failing because "https://example.com/" != "https://example.com:80/"
.
Answered By - Wilfred Hughes
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.