不接受 cookie 的客户端需要 CSRF 保护吗?

信息安全 Web应用程序 csrf
2021-08-11 21:02:30

我最近将一个网站从 Django 1.0 更新到 1.3。引入的更改之一是针对 CSRF 攻击的自动保护在大多数情况下,这很好用,但我对客户端有一个问题,由于某种原因根本不接受 cookie。虽然拒绝此类客户注册或登录是可以的,但他们仍然应该能够使用例如联系表格。Django 拒绝所有没有 CSRF cookie 的 POST 请求,所以我每天都有几十个 CSRF 失败不是攻击,而是不接受 cookie 的合法客户端。

由于这是不可接受的,我的计划是修改 Django 的行为(通过扩展 CSRF 中间件),使其允许通过根本没有任何 cookie 的 POST 请求。我的理由是 CSRF 攻击利用会话 cookie 来伪造请求。但是无论如何都没有附加到请求的会话 cookie,因此在这种情况下不需要针对 CSRF 的保护。

那么,我的推理是否合理,或者我是否要在我的网站上撕下一个巨大的安全漏洞?

4个回答

Django 拒绝所有 POST 请求

只有启用了 csrf 机制。Django 在两种情况下发出 csrf cookie:

  1. 如果您在settings.py. 根据WSGI 规范,中间件适用于所有请求。
  2. @csrf_protect如果您使用, 而不使用中间件来装饰单个视图

在 1 的情况下,所有视图都通过这种机制受到保护,并且将{% csrf_token %}input包含 csrf 令牌的表单中的字段)与发送的 cookie 进行比较。

您有两个选项可以禁用此功能,因此:

  1. 站点范围 - 删除中间件,您的 POST 请求将重新开始工作。
  2. 通过仔细使用,@csrf_exempt您可以选择某些视图(POST url)退出 csrf 保护机制。

那么不使用csrf保护有风险吗?这取决于您的 POST 请求是什么。阅读OWASP对 csrf 的描述- 基本上这个想法是您构建一个请求并说服用户执行它;当他们这样做时,他们的数据会以他们不打算发生的方式进行修改。令牌比较背后的想法是,只有发出的网页才会有有效的令牌,这意味着应该拒绝恶意构造的请求。

因此,答案是肯定的,这很可能是一种风险。对于大多数基于 Web 的应用程序和站点,您确实需要这种保护。

我的计划是修改 Django 的行为(通过扩展 CSRF 中间件),它允许通过根本没有任何 cookie 的 POST 请求。

由于上述原因,我建议不要这样做。我认为您的意思是从现有框架扩展(您不需要这样做),但我只想涵盖其他用户可能考虑的另一种情况,即修补 django 框架本身来做到这一点。在这种情况下...不要,因为除非每个站点都独立存在,否则virtualenv您将立即将此漏洞扩展到使用该代码的所有站点,直到您更新 django 安装。

完全删除 CSRF 保护意味着您不能信任日志信息来显示请求的真实来源(例如 IP 地址)。在您的情况下,这可能没问题。

另一种方法是将一些随机信息添加到隐藏字段。

然后将此信息与一个只有服务器知道的秘密字符串连接起来。哈希可以用作无状态的 CSRF 令牌。根据您的用例,向隐藏字段和哈希添加时间戳可能是个好主意,然后检查它是否不超过半小时。

要验证令牌,只需根据隐藏字段和秘密再次计算哈希即可。

我不知道你的计划有什么毁灭性的问题。但是,让我详细说明我的推理和潜在风险,以便您可以对与消除 CSRF 防御相关的风险水平形成自己的看法。

让我从一个问题开始:您是说您的服务器将在收到来自世界上任何人的 POST 请求时发送一​​封电子邮件,没有会话 cookie、没有身份验证、没有授权?世界上任何向您发送 POST 请求的人都会导致服务器发送一封电子邮件,每个 POST 请求一封电子邮件?如果是这样,这意味着恶作剧的人可能会导致您的服务器发送大量电子邮件,从而向可怜的收件人发送垃圾邮件。这是否是一个严重的问题,我不知道。您必须根据自己的风险承受能力自行决定。

一般来说,任何时候 POST 请求都可能在您的服务器上触发一些副作用,或者可能导致您的服务器采取一些敏感的操作,而无需身份验证,您可能需要仔细考虑以确保您不会向垃圾邮件敞开大门/拒绝服务攻击。

如果您删除 CSRF 保护,则会增加风险。这意味着攻击者可以制作恶意广告,当查看该广告时,会导致查看者的浏览器自动向您的服务器发送 POST 请求,从而触发副作用。这是一种非常便宜的方式,攻击者可以向您的服务器生成大量 POST 请求,但这种方式不会提供任何简单的方法来阻止恶意请求的泛滥(因为每个请求都来自不同的 IP 地址)或追踪攻击者。CSRF 令牌使这种攻击更加困难:现在攻击者必须从他自己的机器上发送 POST 请求,并且不能利用广告来利用他人作为 patsy。

增加的风险相当适中。此外,如果你经营的是一个低价值的低调网站,也许没有人会打扰你,所以也许这不值得担心。我只是想指出风险,即使它在您的特定情况下可以忽略不计。

我开始对 Hendrik Brummermann 的回答添加评论,但很快就用完了空间,因此....

我不明白为什么缺少 CSRF 保护会影响 IP 地址的记录。即使确实如此,在没有任何会话管理的情况下,IP 地址与管理事务的完整性有什么关系?

此外,为了避免重放,令牌需要在事务之间变化 - 因此服务器需要为每个操作生成新值并在请求之间记住它们 - 暗示会话 - 暗示 cookie!

最初的问题缺少的是威胁模型的详细信息——尤其是在没有任何会话的情况下,您要保护的是什么?

假设重放攻击不是一个大问题,那么您可以同时包含一个随机值 (R) 和该随机值的哈希 + salt (f(R+X)) 作为隐藏字段。在 R 中嵌入时间戳会减少任何重放攻击的窗口(当您添加机器来检查它时)。

无状态解决方案可以通过使用在每个请求中提供的信息来实现,例如使用用户代理和接受标头以及(例如)客户端 IP 地址的前 16 位和一个秘密盐。

请注意,我经常指出使用 IP 地址信息进行身份验证的愚蠢行为 - 并且客户端 IP 地址可以更改(例如,当通过负载平衡代理连接时)但是 IME,IP 地址的最高有效位的唯一时间变化是当客户故意试图通过诸如 TOR 之类的服务来掩盖他们的身份时——在这种情况下,您可能无论如何都不想要他们的 POST。

还有其他方法可以使这个用户标识符更加唯一——例如协商的 SSL 属性、当前的 cookie 集(即使它是空的!),

有关客户端指纹识别的更多讨论,另请参阅panopticlick - 但请记住,不接受 cookie 的用户和不会运行您的 javascript 的用户之间可能有很多重叠。

显然,无论您使用什么参考数据进行检查,如果您想通过 Django 来实现,那么您需要同时覆盖令牌生成器和检查器(CsrfViewMiddleware)。