【问题标题】:Django's SuspiciousOperation Invalid HTTP_HOST headerDjango 的 SuspiciousOperation 无效的 HTTP_HOST 标头
【发布时间】: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 标头的良性案例?

【问题讨论】:

标签: django


【解决方案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 配置:stackoverflow.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 攻击的网站。
猜你喜欢
  • 2013-09-10
  • 2013-07-22
  • 2019-09-29
  • 2014-04-20
  • 2017-03-27
  • 2016-07-11
  • 2021-01-20
  • 2014-10-11
相关资源
最近更新 更多