我在Laravel应用程序中打开了两个选项卡。
我点击";注销";在一个选项卡中;注销";在第二个选项卡中。第二个logout
曾经给出419错误,但我将其添加到了VerifyCsrfToken
中间件中的排除列表中。我不明白为什么我需要CSRF保护才能注销。
但是,现在我有一个不同的问题。
在两个选项卡中注销后,两个选项卡都位于登录页面上。现在,如果我试图从第一个选项卡重新登录,我会收到419错误。从第二个选项卡,登录操作正常。
我该如何处理?我不想在用户点击"时向他们显示错误;"登录";,这是糟糕的用户体验。我也不想将登录路由排除在CSRF保护之外。
注意事项
在这个答案中,我将您的浏览器打开的选项卡命名为选项卡A和标签B
只需考虑以下规则:
- 您的浏览器一次为域激活一个会话(使用session_idcookie访问(
- 客户端:每个打开的选项卡都可以有一个CSRF令牌,其形式、标题
- 服务器端:会话变量中只有一个csrf令牌
- 对于每个post请求,客户端和服务器令牌应该匹配,否则会出现419错误
注销时CSRF保护的必要性
没有必要在注销路由中阻止CSRF保护,因为它不是资源更改路径。CSRF Protection的CCD_ 3是件好事。
正在发生的场景
1
首先,您打开了选项卡A和B,它们已登录并共享相同的会话和令牌
选项卡A-会话1-令牌x选项卡B-会话1-令牌x
2
单击选项卡A注销。它将从服务器清除会话。重定向回登录页面。在登录页面中,客户端请求服务器,为他创建了一个新的会话和令牌。
选项卡A-会话2-令牌y选项卡B-会话2(带有session_id的cookie用于域(-令牌x(客户端:显示在选项卡中/从服务器中删除,因为它在会话1中(
由于服务器端没有会话1,该会话持有令牌x因此选项卡B令牌与服务器y令牌不匹配
3
在选项卡B上发送注销。
如果您在启用csrf的情况下发送登出。中间件向您发送419,因为客户端上的令牌x和服务器上的令牌b之间存在错误。
如果其免于csrf,则**清除服务器端的会话**并将客户端重定向到登录页面,然后重定向服务器使用选项卡B的新csrf令牌创建新的会话。
我假设您在注销路由上免除csrf保护,然后继续第二种情况。
选项卡A-会话3-令牌y选项卡B-会话3-令牌z
如您所见,服务器具有会话3,令牌为z。因此选项卡一个匹配错误
结论
正如你所看到的,这种不匹配有一些原因:
- 会话(cookie(是浏览器范围,但令牌是选项卡范围
- 用户在一个选项卡上做了一些事情,使其他选项卡有一个错误处理其表单令牌的会话
所以正如你所看到的,预防解决方案已经出现:
如果用户想要:
- 打开多个选项卡
- 在一个选项卡上注销(重新创建会话(
- 继续使用其他打开的选项卡/表单(刷新可以解决问题(
解决方案
您必须使会话和令牌范围同步
有两种解决方案:
-
使令牌浏览器范围:使用XSRF而不是CSRF,这可以通过使用SPA客户端框架来实现。(优选(
-
使会话页面范围:
-
将注销功能更改为不清除会话,只清除响应session_id
-
使用隐藏输入(其本身向下(进行会话跟踪(obsolote(
-
我建议使用客户端的单页应用程序框架
避免"419页过期":
- 转到中间件
- VerifyCsrfToken.php
- 创建新方法句柄
use Closure;
use IlluminateSupportFacadesAuth;
public function handle($request, Closure $next)
{
if($request->route()->named('logout')) {
if (!Auth::check() || Auth::guard()->viaRemember()) {
$this->except[] = route('logout');
}
}
return parent::handle($request, $next);
}