【发布时间】:2018-07-12 12:22:37
【问题描述】:
当用户访问我的登录页面时,我会生成一个 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 令牌匹配并登录。但是,如果用户进入登录页面并将其打开几个小时,然后返回并尝试如果在不刷新页面的情况下提交表单,那么 PHP 会话将过期并导致表单失败。
我假设它失败是因为 $_SESSION['csrf_token'] 变量为空,因为会话本身不再存在。这就是问题所在。
我找到了一些可行但看起来相当“hacky”的解决方案。
一种解决方案是当用户在一些不活动后返回页面时刷新页面:
<META HTTP-EQUIV="REFRESH" CONTENT="csrf_timeout_in_seconds">
...另一个解决方案是使用来自sessionid 的散列和服务器端机密来创建 CSRF 令牌:
csrf = hash(sessionid+secret)
但是,我认为这也会遇到我目前遇到的同样问题,因为 session_id 也存在于会话中,这意味着会话到期时它也不会存在。
使用“保质期”较长的 PHP Cookie 代替寿命较短的 PHP 会话 Cookie 会不会有问题?有没有我遗漏的替代解决方案?
【问题讨论】: