Django 的 ALLOWED_HOSTS 变量实际上是做什么的?

信息安全 http
2021-08-20 14:41:55

我应该ALLOWED_HOSTS在我的 Django 项目的配置中设置属于我的主机名

... 防止攻击者通过提交带有虚假 HTTPHost标头的请求来毒化带有指向恶意主机的链接的缓存和密码重置电子邮件,即使在许多看似安全的网络服务器配置下也是可能的。

这实际上意味着什么?为什么攻击者不简单地使用“正确的”主机标头并做同样的事情来垃圾密码重置?“中毒缓存”听起来也像是夏娃和鲍勃之间的事情,无论如何我都无法控制......

2个回答

我花了一些时间才找到它,但我发现这似乎回答了这个问题:

https://www.djangoproject.com/weblog/2013/feb/19/security/#s-issue-host-header-poisoning

问题:主机头中毒

以前的几个 Django 安全版本已尝试解决 HTTP Host 标头的持久性问题。Django 包含代码——Django 本身附带的一些功能使用该代码——用于根据传入的 HTTP 请求构造一个完全限定的 URL。根据配置,这会使用 Host 标头,因此可以使 Django 应用程序响应任意 Host 标头的攻击者可以导致 Django 生成并向最终用户显示任意域上的 URL。

此问题的先前迭代(参见 CVE-2011-4139 和 CVE-2012-4520)侧重于加强 Django 对 Host 标头的解析,以消除攻击者使用的各种手段——在提交的 Host 中使用用户名/密码对等技术标头——可以利用这一点。

然而,最终,仅 Django 无法确保攻击者无法提交并导致 Django 接受任意 Host 标头。在已部署的 Django 实例中正确保护这一点还需要配置 Web 服务器,并且配置和可实现的安全级别都因所使用的服务器而异。

鉴于此,django.http.HttpRequest 的 get_host() 方法现在将根据新设置 ALLOWED_HOSTS 验证 Host 标头。此设置是一个字符串列表(默认情况下,一个空列表),对应于标头的可接受值,并支持通配符。

不使用 get_host() 的代码,或可以在不依赖 HTTP 标头的情况下确定当前主机名的 Django 配置(即,启用 Django 的站点框架时)将不受此更改的影响。

由于这是对先前问题的强化/收紧,因此它没有新的 CVE 编号。

James Kettle 提供了一篇关于Practical HTTP Host Header Attacks的好文章。此处对它们进行了总结,并描述了 Django ALLOWED_HOSTS 策略。

漏洞

密码重置矢量

文档中提到的密码重置电子邮件漏洞是 Django 和/或其应用程序使用并信任客户端提供的 HTTP Host 值的结果(听起来是个坏主意?)。Host 值被直接复制到密码重置电子邮件中,提供了一个方便的注入向量。

缓存中毒

缓存中毒变体依赖于这种行为,也依赖于服务器和缓存解决方案处理 HOST 标头的方式的差异。

HTTP_SERVER 与 HTTP['HOST']

正如 Kettle 所描述的,由于 RFC2616,兼容的服务器可以使用 HTTP_SERVER 和 HTTP['HOST'] 之间的差异来传递任意主机字符串。

主机解析

通过利用不同的格式和使用特殊字符,攻击者可以导致“允许的”主机在不同的上下文中被恶意解释。这又是信任主机字符串的应用程序中的错误。

Django 解决方案

正如 Catskul 提供的发行说明所总结的那样,Django 的解决方案是让用户将允许的主机直接放入项目代码中。通过禁止任何其他不匹配ALLOWED_HOSTS的主机,注入向量被消除(“白名单”方法)。

正如詹姆斯指出的那样,这是一个笨拙的解决方案,但它也非常有效。

记住

可以在项目设置中使用通配符,在这种情况下,此功能无助于保护任何东西。

安全的应用程序开发人员将避免以任何有意义的方式使用 HTTP Host 值。

参考

卡洛斯·布埃诺,缓存中毒 (2008)