42

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

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

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

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

4

5 回答 5

40

如果您根据表单请求执行此操作 - 那么您基本上消除了发生 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!!!
}

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

于 2012-05-06T09:45:27.667 回答
21

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

如果您担心令牌可能会泄漏到攻击站点(例如通过 XSS)或被攻击站点获取,您可以将令牌的有效性限制在某个时间范围、某个表单/URL 或一定数量的使用范围内。

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

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

于 2012-05-06T10:28:16.910 回答
2

您的问题的答案是:视情况而定。

而且您不需要将会话用于定时令牌,您只需使用服务器时间和服务器上的密钥即可。

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

不,您需要一个能够在某个时间范围内生成令牌的例程。假设您将时间划分为每 30 分钟。您在表单中为当前 30 分钟创建一个令牌。

提交表单时,您现在和之前的 30 分钟期间验证令牌。因此,令牌的有效期为 30 分钟到一小时。

$token = function($tick = 0) use($secret, $hash) {
    $segment = ((int) ($_SERVER['REQUEST_TIME'] / 1800)) + $tick;
    return $hash($secret . $segment);
};
于 2012-05-06T00:40:19.203 回答
1

owasp网站它提到

每个请求令牌比每个会话令牌更安全,因为攻击者利用被盗令牌的时间范围很小。但是,这可能会导致可用性问题。

它的结论是

CSRF 令牌应该是:

  • 每个用户会话唯一。
  • 秘密
  • 不可预测(安全方法生成的大随机值)。
于 2020-09-02T04:53:38.500 回答
0

您可以使用这两种方法。

1)每个请求的随机令牌。并且为了解决令牌不同步的问题,您可以使用服务器发送的事件http://www.w3.org/TR/eventsource/或 websockets http://www.w3.org/TR/websockets/通信技术来更新每个页面的实时令牌。

2)您可以为每个用户会话使用一个更易于实施的令牌(无需每小时执行一次)。你不会有同步的问题,但如果令牌不是太强,它可以被猜到。当然,如果令牌是非常随机的,那么受恶意攻击的人将很难真正进行请求伪造。

PS第一种方法是最安全的,但它更难实现并且使用更多的资源。

于 2012-05-07T21:03:20.310 回答