每个请求是否有新的 CSRF 令牌?

2022-08-30 11:03:17

所以我正在阅读,并且对拥有CSRF令牌感到非常困惑,无论我应该为每个请求生成一个新令牌,还是每小时或更多?

$data['token'] = md5(uniqid(rand(), true));
$_SESSION['token'] = $data['token'];

但是,假设每小时生成一个令牌会更好,那么我需要两个会话:令牌,过期,

我将如何将其处理到表格中?只需将 echo $_SESSION['token'] 放在隐藏值表单上,然后在提交时进行比较?


答案 1

如果你按照表单请求这样做 - 那么你基本上消除了CSRF攻击发生的能力,你可以解决另一个常见问题:多表单提交

简单来说 - 如果用户在提交之前要求表单,您的应用程序才会接受表单输入。

正常场景:用户A访问您的网站,并要求表单A,获得表单A以及表单A的唯一代码。当用户提交表格A时,他/她必须包括仅用于表格A的唯一代码。

CSRF 攻击场景:用户A访问您的网站,并要求填写表格A。与此同时,他们访问了另一个“坏”网站,该网站试图对他们进行CSRF攻击,让他们提交伪造的表格B。

但是您的网站知道用户A从未要求过表单B - 因此即使他们拥有表单A的唯一代码,表单B也会被拒绝,因为他们没有表单B的唯一代码,只有表单A代码。您的用户是安全的,您可以在晚上轻松入睡。

但是,如果您将其作为通用令牌进行,持续一个小时(就像您上面发布的那样) - 那么上面的攻击可能会起作用,在这种情况下,您的CSRF保护并没有取得多大成就。这是因为应用程序不知道表单B从一开始就没有被要求。它是一个通用令牌。CSRF预防的全部意义在于使每个表单令牌对该表单是唯一的

编辑:因为您要求提供更多信息:1 - 您不必根据表单请求执行此操作,您可以按小时/会话等进行。该点是提供给用户的机密值,并在返回时重新提交。其他网站不知道此值,因此无法提交虚假表格。

因此,您可以为每个请求或每个会话生成令牌:

// Before rendering the page:
$data['my_token'] = md5(uniqid(rand(), true));
$_SESSION['my_token'] = $data['my_token'];

// During page rendering:
<input type="hidden" name="my_token" id="my_token" value="<? php echo $_SESSION['my_token']?>" />

// After they click submit, when checking form:
if ($_POST['my_token'] === $_SESSION['my_token'])
{
        // was ok
}
else
{
          // was bad!!!
}

因为它是“每个表单” - 你不会得到双重表单提交 - 因为你可以在第一个表单提交后擦除令牌!


答案 2

通常,每个用户或每个会话都有一个令牌就足够了。重要的是,令牌仅绑定到一个特定的用户/会话,而不是全局使用。

如果您担心令牌可能被泄露或被攻击站点(例如XSS)获取,则可以将令牌的有效性限制在特定的时间范围,特定的形式/URL或一定量的使用。

但是限制有效性有一个缺点,即它可能导致误报,从而限制可用性,例如,合法请求可能使用刚刚失效的令牌,因为令牌请求太久以前或令牌已经被使用得太频繁。

因此,我的建议是每个用户/会话只使用一个令牌。如果您想要进一步的安全性,请为每个表单/每个用户/会话使用一个令牌,这样如果一个表单/URL的令牌泄露,其他令牌仍然是安全的。


推荐