【问题标题】:Django ALLOWED_HOSTS IPs rangeDjango ALLOWED_HOSTS IP 范围
【发布时间】:2016-08-30 03:05:28
【问题描述】:

有没有办法在 django 中设置一系列 ALLOWED_HOSTS IP?

类似这样的:

ALLOWED_HOSTS = ['172.17.*.*']

【问题讨论】:

  • 我正要回答“是”,但做了一些谷歌挖掘,找不到有人这样做的具体例子。根据docs,您可以在列表中使用单独的通配符,例如ALLOWED_HOSTS = ['*'],(出于安全原因不建议这样做),但我还没有看到像您的示例这样的东西。我仍然倾向于“是”,但在我看到明确说明这一点的参考资料之前,我不会相信。这可能是一个愚蠢的问题,但是您是否尝试过它是否会在 django 中引发任何错误?
  • 最好将其卸载到一个可以有效处理此问题的 Web 服务器。或者甚至可以设置防火墙规则,堆栈越高的流量可以被过滤得越好。
  • @Nez,是的,我愿意。下面看看我的回答。我找到了解决这个问题的方法。
  • 不错。我知道可以做到,但我不知道我在寻找什么样的解决方案。中间件是有道理的。 @serg 的建议应该在堆栈中处理得更高,但这对于流量相对较低的构建应该没问题,对吧?
  • @serg 的建议绝对比 middlware 更适合高负载项目。但我一直在寻找 django 级别的解决方案。

标签: django django-settings


【解决方案1】:

不,这目前是不可能的。根据the docs,支持以下语法:

['www.example.com']  # Fully qualified domain
['.example.com']  # Subdomain wildcard, matches example.com and www.example.com 
['*']  # Matches anything

如果您查看validate_host 方法的实现,您会发现允许单独使用'*',但不允许使用* 作为通配符作为字符串的一部分(例如'172.17.*.*')支持。

【讨论】:

    【解决方案2】:

    我在 Django 上发布了一张票,但是我发现这可以通过执行以下操作来实现

    from socket import gethostname, gethostbyname 
    ALLOWED_HOSTS = [ gethostname(), gethostbyname(gethostname()), ] 
    

    更新。如果您使用的是 docker,下面的代码会更好,因为 gethostbyname 没有得到正确的信息。

    from socket import gethostname, gethostbyname, gethostbyname_ex
    ALLOWED_HOSTS = [ gethostname(), ] + list(set(gethostbyname_ex(gethostname())[2]))
    

    它被转换为集合的原因是 gethostbyname_ex 可以返回重复项。

    django网站上的ticket链接是。

    https://code.djangoproject.com/ticket/27485

    【讨论】:

    • Windows 10 的秋季创建者更新为我打破了这种方法,因为 gethostname() 正在返回 Hyper-V 以太网交换机地址。您可以改用ALLOWED_HOSTS = [ gethostname(), ] + gethostbyname_ex(gethostname())[2]
    • 这真的很有帮助,在 k8s 集群上运行 Django,该节点在运行之前是未知的。
    • 不安全吗?
    • @KarlZillner 不,那不是真的。它只会添加您机器的 IP 地址,没有域名将起作用,他们将收到 Invalid HTTP_HOST 错误。其中“*”将允许所有 ipaddress 和所有域
    • 不幸的是,这只允许添加机器的 IP 和主机名,而不是我们信任但不知道静态 IP 地址的任何其他服务器,例如亚马逊负载均衡器。如果可以添加通配符,我可以允许子网范围,但现在我必须使用 nginx 来解决这个问题来重写主机。
    【解决方案3】:

    Mozilla 发布了一个名为 django-allow-cidr 的 Python 包,旨在解决这个问题。

    announcement blog post 解释说,它对于没有 Host 标头且仅使用 IP 地址的运行状况检查非常有用。

    您必须将您的 IP 地址 '172.17.*.*' 稍微更改为 CIDR range,例如 172.17.0.0/16

    【讨论】:

    • 请注意,这将允许像172.17.malicious.host.com这样的恶意内容
    • 这对我有用;由于原始主机位于内部子网上,因此我的运行状况检查失败。将10.0.0.0/16 添加到ALLOWED_CIDR_NETS 效果很好。ALLOWED_CIDR_NETS = ['10.0.0.0/16']
    • @Cole 不会。查看源代码github.com/mozmeao/django-allow-cidr/blob/master/allow_cidr/… 可以看到它只会匹配特定的IP。 self.allowed_cidr_nets = [IPNetwork(net) for net in allowed_cidr_nets] for net in self.allowed_cidr_nets: 您可以在“使用生成器确保高效处理大型 IP 子网”下查看有关 IPNetwork netaddr.readthedocs.io/en/latest/tutorial_01.html#slicing 的更多信息。
    【解决方案4】:

    这是一个快速而肮脏的解决方案。

    ALLOWED_HOSTS += ['172.17.{}.{}'.format(i,j) for i in range(256) for j in range(256)]
    

    【讨论】:

    • ['172.17.{}.{}'.format(i,j) for i in range(256) for j in range(256)] 与上面使用格式相同。喜欢。
    • ['192.168.1.{}'.format(i) for i in range(256)] 谢谢,我只是想指出你也可以这样做,如果你知道像我一样只需要最后一个数字。
    • 调试不是个好主意,转储的环境看起来很脏
    【解决方案5】:

    我找到了过滤 IP 范围的解决方案:

    https://stackoverflow.com/a/36222755/3766751

    使用这种方法,我们可以通过任何方式过滤 IP(例如使用正则表达式)。

    from django.http import HttpResponseForbidden
    
    class FilterHostMiddleware(object):
    
        def process_request(self, request):
    
            allowed_hosts = ['127.0.0.1', 'localhost']  # specify complete host names here
            host = request.META.get('HTTP_HOST')
    
            if host[len(host)-10:] == 'dyndns.org':  # if the host ends with dyndns.org then add to the allowed hosts
                allowed_hosts.append(host)
            elif host[:7] == '192.168':  # if the host starts with 192.168 then add to the allowed hosts
                allowed_hosts.append(host)
    
            if host not in allowed_hosts:
                raise HttpResponseForbidden
    
            return None
    

    感谢@Zorgmorduk

    【讨论】:

      【解决方案6】:

      如果我们看看 Django 如何验证主机,我们可以深入了解如何使ALLOWED_HOSTS 条目更灵活:

      def validate_host(host, allowed_hosts):
          """
          Validate the given host for this site.
      
          Check that the host looks valid and matches a host or host pattern in the
          given list of ``allowed_hosts``. Any pattern beginning with a period
          matches a domain and all its subdomains (e.g. ``.example.com`` matches
          ``example.com`` and any subdomain), ``*`` matches anything, and anything
          else must match exactly.
      
          Note: This function assumes that the given host is lowercased and has
          already had the port, if any, stripped off.
      
          Return ``True`` for a valid host, ``False`` otherwise.
          """
          return any(pattern == '*' or is_same_domain(host, pattern) for pattern in allowed_hosts)
      
      . . .
      
      def is_same_domain(host, pattern):
          """
          Return ``True`` if the host is either an exact match or a match
          to the wildcard pattern.
      
          Any pattern beginning with a period matches a domain and all of its
          subdomains. (e.g. ``.example.com`` matches ``example.com`` and
          ``foo.example.com``). Anything else is an exact string match.
          """
          if not pattern:
              return False
      
          pattern = pattern.lower()
          return (
              pattern[0] == '.' and (host.endswith(pattern) or host == pattern[1:]) or
              pattern == host
          )
      

      这是一个RegexHost 实用程序,可以通过此验证。

      class RegexHost(str):
          def lower(self):
              return self
      
          def __init__(self, pattern):
              super().__init__()
              self.regex = re.compile(pattern)
      
          def __eq__(self, other):
              # override the equality operation to use regex matching
              # instead of str.__eq__(self, other) 
              return self.regex.match(other)
      

      可以这样使用:

      # this matches '172.17.*.*' and also many impossible IPs
      host = RegexHost(r'172\.17\.[0-9]{1,3}\.[0-9]{1,3}')
      
      # Un-comment the below assertions to prove to yourself that this host
      # validation works. Do not leave these assertions active in 
      # production code for startup performance considerations.
      
      # assert all(host == f'172.17.{i}.{j}' for i in range(256) for j in range(256))
      # assert not any(host == f'172.18.{i}.{j}' for i in range(256) for j in range(256))
      ALLOWED_HOSTS = [host]
      

      【讨论】:

      • 您是否建议使用 65K+ 项数组来验证主机?
      • 没有?我希望有人看到这些断言,确认它们通过,并将它们从设置文件中删除(如果这是他们正在测试的地方)。
      • 请记住,这只会在服务器启动时运行,因此即使他们确实将其保留在设置文件中,应用程序的性能也不会受到影响。我会将它们注释掉,以防有人对此感到困惑。
      猜你喜欢
      • 1970-01-01
      • 2014-08-23
      • 1970-01-01
      • 2018-01-04
      • 2021-10-28
      • 1970-01-01
      • 2013-06-03
      • 2018-04-26
      • 1970-01-01
      相关资源
      最近更新 更多