【问题标题】:Session not writing fast enough to prevent duplicate form submissions on backend. (Laravel)会话写入速度不够快,无法防止后端重复提交表单。 (拉拉维尔)
【发布时间】:2019-10-31 13:01:53
【问题描述】:

我有一个createOrder 请求,目前有点慢(大约 3 - 4 秒)。我正在尝试使用 CSRF 令牌或自定义令牌来防止重复提交表单。

对于这种情况,我正在使用带有文件驱动程序的代客服务......所以这很可能是问题所在,但我尚未将此代码推送到生产服务器,因为它没有按预期工作。

它是这样工作的:

  • 令牌被添加到会话和提交表单中的隐藏"custom_token" 字段中。

  • 表单已提交,我们会在中间件或控制器本身中进行快速比较。

if(! hash_equals($request->session()->get('custom_token'), $request->custom_token)){
    flash()->error('Order placed twice');
    return redirect('/checkout/thank-you');
}

$request->session()->put('custom_token', Str::random(40));
  • 如果它们不匹配,我们会进行快速重定向(Flash 消息目前仅用于调试)。

如果我从控制器中删除主要的 create orde 调用(即慢速位),则上述方法有效,但其中包括 hash_equals 始终返回 true。

所以我的问题是:

  1. 这可能是由于会话文件驱动程序更新缓慢造成的吗?
  2. 当然,如果是这种情况,即使在生产环境中仍然存在发生这种情况的风险。
  3. 有没有更好的方法可以防止在后端重复提交表单?

【问题讨论】:

  • 为什么!if(! hash_equals 中?
  • 如果哈希不相等,抛出错误?
  • 好的,现在代码是有意义的,但是,不相等并不一定意味着请求被放置了两次。任何人都可以使用检查元素调整 custom_token。
  • 我假设一旦使用 CSRF 令牌,它就不能再次播放,Laravel 会将它自己列入黑名单。但我希望你在你的路由上启用了 VerifyCsrfToken 中间件。
  • 如果hash_equals 一次为真,我们接下来要做的就是用新的custom_token 重写。这与其说是有人入侵了系统,不如说是为了确保普通用户在快速提交时单击提交按钮或按回浏览器并以这种方式重新提交时不会提交两次。

标签: php laravel forms session


【解决方案1】:

有几件事,首先我要弄清楚为什么您的 createOrder 需要这么长时间,3 到 4 秒是很长的时间。使用队列 (https://laravel.com/docs/5.8/queues) 并在其中处理大部分工作。

如果放置在控制器的开头,您的解决方案应该可以正常工作。我可能很厚,但是您想拒绝哈希匹配的那些,因此您需要删除 !

if(hash_equals($request->session()->get('custom_token'), $request->custom_token)){
    flash()->error('Order placed twice');
    return redirect('/checkout/thank-you');
}

$request->session()->put('custom_token', Str::random(40));

我假设您正在创建您的哈希,存储它并将其传递给您的表单,并且它正在 $request 中正确返回到您的控制器?尝试将 custom_token 设置为“fred_bloggs”之类的内容,并确保它正确输入。

【讨论】:

  • 抱歉,谢谢重定向的代码不是很清楚。实际上,如果第一个条件为真,则意味着脚本已被重复请求。因此,如果哈希值不同,我们不应该运行脚本的其余部分。
【解决方案2】:

尝试在控制器代码中使用带有 try catch 的事务:

try{
   \DB::beginTransaction(); //initiate the transaction and lock the database or table
   //execute your desired code here

   \DB::commit(); //commit the changes and unlock the database or table
}catch(\Exception $e){
   \DB::rollback(); //revert to original state if something goes wrong
}

【讨论】:

  • 我认为这是两次提交相同的表单数据,而不是一个数据库插入干扰另一个。(没有投票)
  • 是的,这是正确的,相同的表单数据被发送了两次。
  • 能否在使用 ajax 请求提交之前禁用提交按钮
  • 我们可以(也可以)但也需要可靠的后端保护。
猜你喜欢
  • 1970-01-01
  • 2020-08-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-03
  • 2011-06-04
  • 2016-12-07
相关资源
最近更新 更多