如何在Django的csrf中间件中处理utf-8与punycode问题?

时间:2015-04-28 19:09:56

标签: python django django-csrf punycode

我有一个非{ascii字符的域名,类似于http://blå.no该域名注册了其punycode等价物:

response = self.test_client.post(
    request_path,
    data=data,
    follow_redirects=False
)
expectedPath = '/'
self.assertEqual(response.status_code, 302)
self.assertEqual(urlparse(response.location).path, expectedPath)

也在Apache vhost中设置:

xn--bl-zia.no

我看到的问题来自包含以下内容的请求:

<VirtualHost *:443>
    ServerName xn--bl-zia.no
    ...

即。 referer以utf-8发送而不是punycode。我得到的例外是:

'HTTP_USER_AGENT': 'Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko',
'HTTP_HOST': 'xn--bl-zia.no',
'SERVER_NAME': 'xn--bl-zia.no',
'HTTP_REFERER': 'https://bl\xc3\xa5.no/login/ka/?next=/start-exam/participant-login/',
'HTTP_X_REQUESTED_WITH': 'XMLHttpRequest',

Traceback (most recent call last): File "/srv/cleanup-project/venv/dev/lib/python2.7/site-packages/django/core/handlers/base.py", line 153, in get_response response = callback(request, **param_dict) File "/srv/cleanup-project/venv/dev/lib/python2.7/site-packages/django/utils/decorators.py", line 87, in _wrapped_view result = middleware.process_view(request, view_func, args, kwargs) File "/srv/cleanup-project/venv/dev/lib/python2.7/site-packages/django/middleware/csrf.py", line 157, in process_view reason = REASON_BAD_REFERER % (referer, good_referer) UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 10: ordinal not in range(128) 中的相关代码是:

csrf.py

good_referer = 'https://%s/' % request.get_host() if not same_origin(referer, good_referer): reason = REASON_BAD_REFERER % (referer, good_referer) 使用请求中的get_host()

是否有本地Django方法来处理这个问题,或者我是否需要编写一个中间件,将utf-8转换为referer头部分域中的punycode?

1 个答案:

答案 0 :(得分:0)

这是一个中间件解决方案..

import urlparse


class PunyCodeU8RefererFixerMiddleware(object):
    def process_request(self, request):
        servername = request.META['SERVER_NAME']
        if 'xn--' not in servername:
            return None

        referer = request.META.get("HTTP_REFERER")
        if not referer:
            return None

        url = urlparse.urlparse(referer)
        try:
            netloc = url.netloc.decode('u8')
        except UnicodeDecodeError:
            return None

        def isascii(txt):
            return all(ord(ch) < 128 for ch in txt)

        netloc = '.'.join([
            str(p) if isascii(p) else 'xn--' + p.encode('punycode')
            for p in netloc.split('.')
        ])
        url = url._replace(netloc=netloc)
        request.META['HTTP_REFERER'] = urlparse.urlunparse(url)
        return None

当它发现它无法做任何有用的事情时,它会尽快保释。当然必须在csrf中间件之前安装。