Django 的 SuspiciousOperation 无效的 HTTP_HOST 标头

Posted

技术标签:

【中文标题】Django 的 SuspiciousOperation 无效的 HTTP_HOST 标头【英文标题】:Django's SuspiciousOperation Invalid HTTP_HOST header 【发布时间】:2013-02-20 17:19:48 【问题描述】:

升级到 Django 1.5 后,我开始收到如下错误:

Traceback (most recent call last):

File "/usr/local/lib/python2.7/dist-packages/django/core/handlers/base.py", line 92, in get_response
response = middleware_method(request)

File "/usr/local/lib/python2.7/dist-packages/django/middleware/common.py", line 57, in process_request
host = request.get_host()

File "/usr/local/lib/python2.7/dist-packages/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): www.google.com

<WSGIRequest
path:/,
GET:<QueryDict: >,
POST:<QueryDict: >,
COOKIES:,
META:'CONTENT_LENGTH': '',
'CONTENT_TYPE': '',
'DOCUMENT_ROOT': '/etc/nginx/html',
'HTTP_ACCEPT': 'text/html',
'HTTP_HOST': 'www.google.com',
'HTTP_PROXY_CONNECTION': 'close',
'HTTP_USER_AGENT': 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)',
'PATH_INFO': u'/',
'QUERY_STRING': '',
'REMOTE_ADDR': '210.245.91.104',
'REMOTE_PORT': '49347',
'REQUEST_METHOD': 'GET',
'REQUEST_URI': '/',
u'SCRIPT_NAME': u'',
'SERVER_NAME': 'www.derekkwok.net',
'SERVER_PORT': '80',
'SERVER_PROTOCOL': 'HTTP/1.0',
'uwsgi.node': 'derekkwok',
'uwsgi.version': '1.4.4',
'wsgi.errors': <open file 'wsgi_errors', mode 'w' at 0xb6d99c28>,
'wsgi.file_wrapper': <built-in function uwsgi_sendfile>,
'wsgi.input': <uwsgi._Input object at 0x953e698>,
'wsgi.multiprocess': True,
'wsgi.multithread': False,
'wsgi.run_once': False,
'wsgi.url_scheme': 'http',
'wsgi.version': (1, 0)>

我在我的 settings.py 文件中设置了ALLOWED_HOSTS = ['.derekkwok.net']

这里发生了什么?有人冒充 Google 访问我的网站?还是有人错误地设置了 HTTP_HOST 标头的良性案例?

【问题讨论】:

你知道如何解决这个问题吗?面临同样的问题。每天记录大约一百个这样的错误。不知道这是否是我需要担心的事情。 这篇博文提供了一种停止电子邮件的好方法:tiwoc.de/blog/2013/03/… 备用答案***.com/a/25114003/1714030、***.com/a/21170400/1714030、***.com/q/15238506/1714030 和 ***.com/q/18220519/1714030 【参考方案1】:

如果您使用 Nginx 将请求转发到在 Gunicorn/Apache/uWSGI 上运行的 Django,您可以使用以下命令来阻止错误请求。感谢@PaulM 的建议。

upstream app_server 
    server unix:/tmp/gunicorn_mydomain.com.sock fail_timeout=0;


server 

    ...

    ## Deny illegal Host headers
    if ($host !~* ^(mydomain.com|www.mydomain.com)$ ) 
        return 444;
    

    location  / 
        proxy_pass               http://app_server;
        ...
    


【讨论】:

很高兴看到这是对文档的改进hint hint :) @webjunkie,来自您的链接,“在某些情况下,您根本无法避免使用 if,例如,如果您需要测试没有等效指令的变量。”我的示例正确使用它并且在我的生产环境中运行良好。所以总而言之,一定要这样做! :) 您可以轻松避免它:只需指定您需要的 server_name,其余的由默认服务器处理程序处理。 查看此答案以获得类似的 Apache 配置:***.com/a/18792080 来自 webjunkie 提供的链接:“指令如果在位置上下文中使用时出现问题”。 Brent 给出的示例在server 块中使用if,而不是在location 块中。这是否意味着在这种情况下if 可以?【参考方案2】:

当使用 Nginx 时,你可以设置你的服务器,只请求你想首先访问 Django 的主机。这应该不会再给您带来 SuspiciousOperation 错误了。

server 
    # default server

    listen 80;
    server_name _ default;

    return 444;

server 
    # redirects

    listen 80;
    server_name example.com old.stuff.example.com;

    return 301 http://www.example.com$request_uri;

server 
    # app

    listen 80;
    server_name www.example.com; # only hosts in ALLOWED_HOSTS here

    location  / 
        # ...
    
    # ... your config/proxy stuff

【讨论】:

我喜欢这种方法而不是使用 Brent 建议的 if 方法,但我无法让它与端口 443 一起使用。我尝试模仿你的建议(更改了监听端口),并且我的实际 SSL 站点没有加载——它被我添加的这个条目捕获。关于如何解决的任何想法? ServerFault.com 上的另一位发帖人也有类似的问题,所以我按照他的建议,只针对 443 流量使用 if 语句方法 如果您也想捕获 SSL 请求,似乎您必须指定证书文件的路径(即使您只想丢弃):server listen 80 default_server; listen 443; server_name _; ssl_certificate /path/to/file.crt; ssl_certificate_key /path/to/file.key; return 444; 如果请求的HOST无效,Nginx会返回什么? 50 倍还是 40 倍? 这个配置有什么额外的?我在重定向和应用程序部分都设置了服务器名称,我仍然得到Invalid HTTP_HOST header(使用 Django 1.8.x)【参考方案3】:

这已在较新版本的 Django 中得到修复,但如果您使用受影响的版本(例如 1.5),您可以向您的记录器处理程序添加过滤器以摆脱这些问题,如 this 博客文章中所述。

剧透:

from django.core.exceptions import SuspiciousOperation

def skip_suspicious_operations(record):
  if record.exc_info:
    exc_value = record.exc_info[1]
    if isinstance(exc_value, SuspiciousOperation):
      return False
  return True

LOGGING = 
    'version': 1,
    'disable_existing_loggers': False,
    'filters': 
        'require_debug_false': 
            '()': 'django.utils.log.RequireDebugFalse',
        ,
        # Define filter
        'skip_suspicious_operations': 
            '()': 'django.utils.log.CallbackFilter',
            'callback': skip_suspicious_operations,
        ,
    ,
    'handlers': 
        'mail_admins': 
            'level': 'ERROR',
            # Add filter to list of filters
            'filters': ['require_debug_false', 'skip_suspicious_operations'],
            'class': 'django.utils.log.AdminEmailHandler'
        
    ,
    'loggers': 
        'django.request': 
            'handlers': ['mail_admins'],
            'level': 'ERROR',
            'propagate': True,
        ,
    

【讨论】:

实施的修复或版本的任何链接?谢谢 我在版本 2.0.5 上拥有它 这在较新版本的 Django 上不固定。我正在使用 Django 2.0.10【参考方案4】:

如果您的ALLOWED_HOSTS 设置正确,则可能有人通过欺骗标头来探测您的网站是否存在漏洞。

Django 开发人员现在正在讨论将其从 500 内部服务器错误更改为 400 响应。见this ticket。

【讨论】:

我认为更可能的解释是网络爬虫(机器人)只是在端口 80 上爬取公共 IP 地址 - 在这种情况下,您会希望允许它们。 @markmnl 合法的网络爬虫不应该伪造主机头。 它只是使用 IP 地址而不是域名进行连接,并且 IP 地址不在 ALLOWED_HOSTS 中 - 或者至少这是发生在我身上的事情 - 我可以通过将浏览器指向它来复制它IP 地址。 是的。在任何半繁忙的站点中,这种情况每天都会发生。他们现在已经修复了它,但是这里有一个“插入式”应用程序,它可以在所有版本中对它进行分类,并带有一个错误率过滤器。 github.com/litchfield/django-safelogging 在互联网上部署我的网站后。我发现很多人试图使用无效主机访问我的网站。不仅使用IP地址。我想这可能是一些人试图找到一个无法防御 csrf 攻击的网站。

以上是关于Django 的 SuspiciousOperation 无效的 HTTP_HOST 标头的主要内容,如果未能解决你的问题,请参考以下文章

django的文档

Django 大神带你飞系列~走进Django

Django:启动django

django的单元测试怎么用

django-admin 和django-admin.py的区别

mac电脑安装django ,运行django报错解决