为什么Django的CSRF无法通过HTTPS工作?

问题描述 投票:3回答:3

我在http://example.com有一个Django网站,可以正常工作,包括发布请求。我添加了HTTPS,因此也可以通过https://example.com访问我的网站。

我可以在HTTPS上加载任何页面,但是在尝试进行POST时总是会收到CSRF验证错误。 POST请求可以在HTTP上正常工作。

我的Django进程在nginx后面带有gunicorn,并且我的nginx设置为X_Forwarded_For。因此,HTTPS请求具有以下标头(取自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',

并且HTTP请求具有以下标头:

'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',

当我在HTTP上没有问题时,为什么CSRF在HTTPS上不起作用?

django https django-csrf
3个回答
10
投票

原来是nginx配置问题。我的服务器设置为:

nginx-> nginx-> gunicorn

在第二个nginx系统上,我有

proxy_set_header        Host            $host:$server_port;

但是,由于HTTPS在第一个nginx处终止,因此$server_port始终为80。

在HTTPS上为Django does strict referer checking(请参见第4点)。查看Django来源:

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验证失败,因为"https://example.com/" != "https://example.com:80/"


0
投票

[就像提到的here,我通过在settings.py中添加以下Django常量解决了这个问题,因此Django考虑了代理标头:

# Setup support for proxy headers
USE_X_FORWARDED_HOST = True
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')

-1
投票

如果您查看CsrfViewMiddleware

Django在request.is_secure()]时检查'Referer'标头

good_referer = 'https://%s/' % request.get_host()
if not same_origin(referer, good_referer):
    reason = REASON_BAD_REFERER % (referer, good_referer)
    return self._reject(request, reason)
© www.soinside.com 2019 - 2024. All rights reserved.