django1.5的ALLOWED_HOSTS相关问题

django1.5有个域名配置问题,日志如下:

[2014-10-14 19:50:07,749][ERROR][django.request][base.py:handle_uncaught_exception:212] Internal Server Error: /
Traceback (most recent call last):
  File "/home/yixiang/lib/pymodules/django/core/handlers/base.py", line 92, in get_response
    response = middleware_method(request)
  File "/home/yixiang/lib/pymodules/django/middleware/common.py", line 57, in process_request
    host = request.get_host()
  File "/home/yixiang/lib/pymodules/django/http/request.py", line 72, in get_host
    "Invalid HTTP_HOST header (you may need to set ALLOWED_HOSTS): %s" % host)
SuspiciousOperation: Invalid HTTP_HOST header (you may need to set ALLOWED_HOSTS): s_www.hustyx.com

从日志可以看到,网站访问者输入了一个奇怪的域名s_www.hustyx.com,有意思的是,django发现了这个错误, 但是却报500错误,即internal server error。这明显不合理,因为很明显django有能力处理这样的状况, 或者报404,或者报400。

这个问题在django1.7中得到了修复,处理方式是返回400错误,即bad request,同时会打印error日志, 日志为django.security级别,表示涉及安全的,需要特别关注的日志。其实我个人不赞成弄一个这么神经兮兮的日志类别, 网络上环境复杂,各种杂乱的请求都有,这种请求返回400错误,打个普通warning日志就足够了,日志多了,根本关注不过来。

这种请求是如何进到django,以及如何出错的呢,下面理一理线路。

首先是域名解析,配置是:

记录类型主机记录记录值TTL
A * xxx.xxx.xxx.xxx 10分钟
A @ xxx.xxx.xxx.xxx 10分钟

A记录配置指向的IP,CNAME记录配置指向的域名,*表示匹配*.hustyx.com,@表示单独匹配hustyx.com。

再就是nginx的配置,如下写法也表示同时匹配*.hustyx.com和hustyx.com:

server_name  .hustyx.com;

这样请求就到了django中,在http/request.py文件中有获取host的代码,如下:

def get_host(self):
    ...
    allowed_hosts = ['*'] if settings.DEBUG else settings.ALLOWED_HOSTS
    if validate_host(host, allowed_hosts):
        return host
    else:
        raise SuspiciousOperation(
            "Invalid HTTP_HOST header (you may need to set ALLOWED_HOSTS): %s" % host)

可见validate_host决定了域名是否通过验证,代码如下:

def validate_host(host, allowed_hosts):
    ...
    if not host_validation_re.match(host):
        return False
    ...

在validate_host函数中有很多验证代码,但最开始做了一个简单的正则验证,上面的域名就验证不通过了, 正则内容为:

host_validation_re = re.compile(r"^([a-z0-9.-]+|\[[a-f0-9]*:[a-f0-9:]+\])(:\d+)?$")

也就是下划线是不符合域名格式要求的,这就是为什么s_www.hustyx.com会验证不通过的原因,而不是ALLOWED_HOSTS的配置问题, DEBUG状态下,ALLOWED_HOSTS默认为:

ALLOWED_HOSTS = ['*']

但还是建议硬编码,写上确切的域名,如下写法跟nginx中配置的意义一样,既匹配*.hustyx.com,也匹配hustyx.com:

ALLOWED_HOSTS = ['.hustyx.com']

也就是说,访问abc.hustyx.com,也是没有问题的,当真有二级域名的需求时,在域名解析或者nginx中配置更安全也更高效。

最终解决方案,是升级到1.7版本呢,还是在1.5版本的基础上修复该问题呢,我选择后者,因为对1.5版本的代码已经相当熟悉了。

发表于 2014年10月20日 16:42   评论:0   阅读:2525  



回到顶部

首页 | 关于我 | 关于本站 | 站内留言 | rss
python logo   django logo   tornado logo