【问题标题】:Does it make sense to use CSRF protection for anonymous users?对匿名用户使用 CSRF 保护有意义吗?
【发布时间】:2015-09-17 13:18:57
【问题描述】:

是否应该为匿名用户使用 CSRF 保护,还是违背了它的目的?

我有一个可以匿名访问的 URL。当使用适当的信息访问 URL 时,我的数据库中的一些值会更新。例如,客户可以在他们的订单确认页面上放置一些代码,该代码将向http://example.com/save-request 发出 POST 请求,并发送以下数据:

{orderId: 1234, referralCode: 'ABCDEF'}

当我收到此请求时,我会使用推荐代码更新我的数据库中的给定订单:

$order = Order::find(Input::get('orderId'));
$order->referral_code = Input::get('referralCode');
$order->save();

我试图保护此 URL 免受滥用,以便用户无法发送随机订单 ID 请求并尝试获取与其相关联的推荐代码。

想到了 CRSF 保护,但这意味着我需要先获取令牌,这需要另一个公共 URL。看起来这会让滥用变得稍微困难​​一些,但仍然有可能,因为滥用者可以简单地获取令牌,然后正常发出请求。

是否有任何策略可以防止此类滥用?

【问题讨论】:

  • 也许这个问题更适合 progammers.stackexchange。可以转过来还是我应该重新发布到那里?
  • 在订单确认页面发出 POST 请求时,您的客户是否已经通过身份验证?或者他们是匿名的?
  • @light 他们是匿名的。

标签: php laravel csrf


【解决方案1】:

我同意 CSRF 令牌对未经身份验证的请求没有用,但它也没有任何害处,也不会增加额外的工作(在 Laravel 的情况下),所以在大多数情况下,没有真正的理由在这里和那里省略它。

关于您的问题的解决方案,请尝试将订单的id 替换为生成的随机order_number 之类的东西,这比简单的ID 更难猜测。

另一种解决方案是这样的:

$order = Order::where('id', '=', Input::get('orderId'))
    ->where('created_at', '=', Input::get('createdAt'))->first();

if ($order) {
    $order->referral_code = Input::get('referralCode');
    $order->save();
}

在这种情况下,用户必须猜测 id 并猜测特定订单的创建时间。

您还可以混合使用这两种解决方案(order_numbercreated_at)。

这不是一个完美的 100% 解决方案,但会大大降低欺诈的可能性。

祝你好运!

【讨论】:

  • 我不太明白 CSRF 令牌如何帮助防止人为错误 w.r.t. CORS。你愿意澄清一下吗?您提供的链接对此没有任何说明。我认为了解“CORS 攻击的工作原理”更重要 - owasp.org/index.php/Cross-Site_Request_Forgery_%28CSRF%29。介绍已经阐明了它的含义。在 OP 的案例中,CSRF 攻击不是主要问题 - 欺诈/滥用预防才是。
  • 我从来没有说过 CSRF 保护是 OP 的主要关注点,所以我完全同意这里。其次,你是对的,我必须从我的答案中删除那个 CORS 废话,因为它是错误的(我重新阅读了我阅读这篇文章的文章)。但我的主要观点是,故意忽略保护比为未经身份验证的请求实施保护更没有意义。这个令牌不会造成任何伤害,也不会增加额外的工作(至少在 Laravel 的情况下),但可能会导致错误(例如,另一个程序员决定向该页面添加身份验证并忘记添加丢失的令牌)。
  • 同意。实现 CSRF 很容易,所有系统都应该尽可能(正确地)实现它,即在登录时分配一个 CSRF 令牌,并期望所有后续请求都包含它(可能在标头中)直到注销。我不认为我们应该故意省略 CSRF,但我确实认为正确实施很重要。例如拥有一个获取 CSRF 令牌的 URL 完全违背了 CSRF 保护的目的——在攻击中,攻击者可能只是使用相同的 URL 来获取令牌。
【解决方案2】:

CSRF 旨在保护经过身份验证的会话。基本思想是:服务器为所有经过身份验证的会话向客户端提供一个 CSRF 令牌。客户端应该在每个后续请求中将相同的 CSRF 令牌传递给服务器。因此,如果一个请求没有令牌,服务器应该忽略/记录它。理想情况下,您的 CSRF 令牌应该只在身份验证时传递给客户端。如果有一个单独的 URL 来获取 CRSF 令牌,那就没有意义了。

在您的情况下,由于用户在“订单确认”时始终是匿名的,因此 CSRF 保护不太适用。我认为最好的策略是对数据和 API 进行建模,这样每个“订单确认”都是一个带有可选“referralCode”的原子请求。然后,您的 API 函数/端点(可能是 /confirm-order)可以获取 referralCode 并将其与任何其他确认处理逻辑一起保存到 Order 对象中。用于编辑订单的 API 函数/端点,可能是 /edit-order,应该需要身份验证。然后,应用标准的 CSRF 保护。

但是,如果您打算允许匿名用户更改他们的订单详细信息,包括 referralCode,您可以通过跟踪更改来减少滥用,并且只允许最大数量的更改。如果有帮助,您还可以添加一些时间限制。

【讨论】:

  • 感谢您对 CSRF 的清晰解释,并确认它对匿名用户没有用处。不幸的是,在这种情况下,订单是通过第 3 方购物车下达的。对我的 URL 的发布请求与下订单的网站的 URL 不同,因此不能选择发出单个请求。我有同样的想法来使用时间限制(即,推荐代码不能在下订单后超过 2 分钟添加)。我认为这是我最好的选择。
  • @flyingL123 所以订单存储在第三方数据库中,而推荐代码数据存储在您的站点数据库中?听起来像是一个很容易被滥用的复杂数据模型。
  • 订单是通过 Bigcommerce 商店下达的。他们得到订单并将其存储在他们的数据库中,然后启动一个 webhook 告诉我有一个新订单。然后我通过他们的 API 提取订单数据并将其保存到我的数据库中。然后我查看是否有该订单的订单推荐请求排队,如果有,则使用我的数据库中的适当推荐代码更新该订单。它比应有的复杂得多,但这就是 Bigcommerce 的工作方式:)
  • 好吧,我想我现在明白了。如果他们到您的服务器的 webhook 允许某种“直通”变量并且您可以控制订单提交表单,您还可以在其中包含身份验证令牌。然后,更新referralCode/save-request 调用也应该包含该令牌作为参数。这有点像验证/save-request 与在 Bigcommerce 下订单的客户来自同一客户。
  • 这真是个好主意。很遗憾,我无法将任何其他信息传递给订单。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-09-13
  • 2020-11-14
  • 1970-01-01
  • 2015-11-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多