Django ALLOWED_HOSTS IP 范围

Posted

技术标签:

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

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

类似这样的:

ALLOWED_HOSTS = ['172.17.*.*']

【问题讨论】:

我正要回答“是”,但做了一些谷歌挖掘,找不到有人这样做的具体例子。根据docs,您可以在列表中使用单独的通配符,例如ALLOWED_HOSTS = ['*'],(出于安全原因不建议这样做),但我还没有看到像您的示例这样的东西。我仍然倾向于“是”,但在我看到明确说明这一点的参考资料之前,我不会相信。这可能是一个愚蠢的问题,但是您是否尝试过它是否会在 django 中引发任何错误? 最好将其卸载到一个可以有效处理此问题的 Web 服务器。或者甚至可以设置防火墙规则,堆栈越高的流量可以被过滤得越好。 @Nez,是的,我愿意。下面看看我的回答。我找到了解决这个问题的方法。 不错。我知道可以做到,但我不知道我在寻找什么样的解决方案。中间件是有道理的。 @serg 的建议应该在堆栈中处理得更高,但这对于流量相对较低的构建应该没问题,对吧? @serg 的建议绝对比 middlware 更适合高负载项目。但我一直在寻找 django 级别的解决方案。 【参考方案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://***.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+ 项数组来验证主机? 没有?我希望有人看到这些断言,确认它们通过,并将它们从设置文件中删除(如果这是他们正在测试的地方)。 请记住,这只会在服务器启动时运行,因此即使他们确实将其保留在设置文件中,应用程序的性能也不会受到影响。我会将它们注释掉,以防有人对此感到困惑。

以上是关于Django ALLOWED_HOSTS IP 范围的主要内容,如果未能解决你的问题,请参考以下文章

用于 Amazon ELB 的 Django ALLOWED_HOSTS

如何将 EC2 ip 地址动态添加到 Django ALLOWED_HOSTS

Django REST api 调用和 allowed_hosts

django之允许外部机器访问

Django如何将自己的网站让其他主机访问

Django让局域网内的人访问