【问题标题】:PHP CSRF Token Expiring With SessionPHP CSRF 令牌随着会话到期
【发布时间】: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 会不会有问题?有没有我遗漏的替代解决方案?

【问题讨论】:

    标签: php jquery ajax session


    【解决方案1】:

    我会考虑增加php.ini 中的session.gc-maxlifetime,这将控制该会话何时到期。

    PHP 文档:http://php.net/manual/en/session.configuration.php#ini.session.gc-maxlifetime

    此外,虽然如果用户的 afk 时间超过该设置,这并不能解决问题,但我会在 X 时间后刷新登录页面而无需任何交互。您还可以考虑为该会话生成一个 CSRF 令牌的端点,然后登录页面将发送一个 ajax 请求以获取令牌,您可以每 X 分钟调用一次该服务,这将更新会话值。

    【讨论】:

      【解决方案2】:

      回答我自己的问题,因为我找到了理想的解决方案

      最终,我需要的是一个比会话持续时间更长但仍然安全的令牌。

      经过大量研究,我发现将 CSRF 令牌存储为浏览器 cookie 是很正常的做法 - 大量网站都这样做,而且同样安全。

      我不希望 cookie 永久存在,因为理论上,如果黑客试图代表用户提交数据(CSRF 攻击),他们最终会破解正确的令牌。

      为了否定这一点,我创建了一个仅在用户浏览器关闭时过期的 cookie——这并不能完全防止 CSRF 令牌被破解,但 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 = TRUE;
          $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);
      
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-02-10
        • 2023-01-24
        • 2012-09-01
        • 2016-03-13
        • 1970-01-01
        • 2015-12-10
        • 2014-11-26
        • 2021-10-23
        相关资源
        最近更新 更多