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版本的代码已经相当熟悉了。