我正在开发一个简单的网站,我正在尝试修复登录页面的问题,如果用户将页面打开几个小时后再返回并尝试使用它,那么csrf令牌不再有效,他们必须刷新页面才能再次开始正常工作。
所以匿名用户来到我的登录页面。该页面将生成一个csrf,如下所示:
if (empty($_SESSION['csrf_token']))
$_SESSION['csrf_token'] = bin2hex(openssl_random_pseudo_bytes(32));
然后在我的表单中显示如下:
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token']; ?>">
到目前为止,这一切都很简单。这是当它开始下山时...当用户点击登录按钮时,会发出ajax请求,该请求使用会话变量检查csrf表单值:
if (!empty($_POST['csrf_token'])) {
if (hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) {
// Success! They matched
} else {
// CSRF session token did not match posted form token
die();
}
} else {
// CSRF token was missing from posted form
die();
}
对于大多数用户来说,这工作正常,csrf令牌匹配并且他们登录但如果用户要进入登录页面,请将页面打开几个小时,然后回来会话将过期所以登录失败。从我猜测它失败,因为$ _SESSION ['csrf_token']是空的,因为它不再存在。这就是问题所在。
我找到了几个可行的解决方案,但没有沿着我正在寻找的路线走下去。一种解决方案是在用户最终使用以下内容恢复使用后刷新页面:
<META HTTP-EQUIV="REFRESH" CONTENT="csrf_timeout_in_seconds">
另一种解决方案是使用会话ID中的散列和服务器端秘密来创建csrf(但我认为这也会遇到同样的问题,因为session_id在尝试时不再存在检查csrf是否有效):
csrf = hash(sessionid+secret)
使用php cookie而不是php会话cookie会有问题吗?
还有其他替代解决方案吗?
答案 0 :(得分:0)
我会考虑增加session.gc-maxlifetime
中的php.ini
,这将控制该会话何时到期。
PHP文档:http://php.net/manual/en/session.configuration.php#ini.session.gc-maxlifetime
此外,虽然如果用户的工作时间超过该设置,但这并不能解决问题,但我会在X时间后刷新登录页面而不进行任何交互。您还可以考虑让端点为该会话生成CSRF令牌,然后登录页面将发送ajax请求以获取令牌,您可以每隔X分钟调用该服务,这将更新会话值。
答案 1 :(得分:0)
最后回复此问题,因为我找到了一个可以在几天后运行的解决方案。
所以我最终需要的是一个令牌,其持续时间比会话长,但不会超过具有设定有效期的普通cookie。
经过相当多的研究后,我发现将csrf存储为cookie是很正常的做法,很多网站都是这样做的。
显然,我并不希望cookie是持久的,因为从理论上讲,如果黑客试图代表你提交数据(提交CSRF攻击),他们最终会破解正确的令牌。
为了避免这个问题,我创建了一个只在浏览器会话结束时才会过期的cookie - 这最终仍然会被破解,但如果关闭窗口会删除它,那么它的可能性就会降低。
我很可能会实施另一种策略来更频繁地清除csrf,但现在它可以正常工作
以下是我所拥有的:
if (empty($_COOKIE['csrf_token'])) {
$cookie_csrf_name = 'csrf_token';
$cookie_csrf_value = bin2hex(random_bytes(32));
$cookie_csrf_expiry = 0;
$cookie_csrf_secure_only = FALSE;
$cookie_csrf_http_only = TRUE;
setcookie($cookie_csrf_name, $cookie_csrf_value, $cookie_csrf_expiry, '/', 'example.com', $cookie_csrf_secure_only, $cookie_csrf_http_only);
/* I did have a problem where the cookie wouldn't be accessible without a refresh so to avoid this issue I found this line works (weirdly enough) */
$_COOKIE[$cookie_csrf_name] = $cookie_csrf_value;
}