为什么防伪令牌需要这么多位?

信息安全 Web应用程序 csrf
2021-09-03 06:46:36

Google 建议使用 130 位加密安全随机数作为防伪令牌。

为什么我们需要这么多位?如果攻击者决定发起蛮力攻击,你难道不能在几次尝试后检测到并锁定他们吗?只需 30 位,您就可以为 1000 个并发用户获得 100 万个可能的令牌。使用蛮力猜测令牌似乎极不可能。

我的观点是,30-40 位的数据似乎很难用蛮力破解。那么为什么 Google 推荐 130 位呢?这不是矫枉过正吗?

更新:好的,说你无法阻止暴力攻击......

  • 根据这篇 Quora 帖子, Facebook 每分钟有 500,000 名独立访问者。
  • 假设每个用户每秒发出一个请求,那么您每 2 微秒就有一个请求。
  • 因此,我们可以安全地假设(截至今天)最强大的攻击者将能够每 2 微秒最多发送一个请求。
  • 接下来,假设我们在 5 分钟后使令牌过期。这意味着攻击者可以在令牌过期之前发出 1.5*10 8个请求。
  • 接下来,假设我们希望攻击者猜到令牌的机会小于 1%。因此,我们需要一个由每个活跃代币组成的 1.5*10 10 个代币池。
  • 因此,如果您有 500,000 个并发用户 (Facebook),您需要 7.5*10 15 个令牌(每个用户一个池)。
  • 这意味着您需要 53 位数据,这与 Google 要求的 130 位数据相差甚远。
4个回答

您的问题做出了一个不应在该领域做出的假设:

尝试几次后,您是否无法检测并锁定它们

是的,在良好的工作环境中,应该有这样的系统来限制各种故障。这是一件好事,但它绝不应该是您的第一道或唯一一道防线如果您的安全模型依赖于这样的额外层来实现其理论安全性,那么您将引入不必要的故障点和可能的攻击向量。

通过将需要完成的理论数学保持在遥不可及的范围内,您可以降低可能(或最终)无法控制的事情所带来的风险,例如错误的速率限制器实施或内部攻击工作。

导致需要这么多熵的其他一些因素以及为什么它们可能建议 30 个字符:

首先,这是为了保护应用程序端的登录操作——然后它将创建自己的独立于 Google 的经过身份验证的会话。重要的是由应用程序来存储令牌并决定何时停止接受它。如果它保存在会话或数据库表中,那么您可能会无限期地继续接受该令牌,而 Google 无法确保应用程序不会这样做,因此他们更容易推荐更多熵。

其次,他们建议在该示例中在 PHP 中使用rand() 。老实说,这让我感到惊讶,因为该函数不会产生加密安全的随机数。事实上,根据文档,它可能只在某些系统上产生 0 到 32767 之间的数字。这向我表明,30 个字符对于确保安全性并不是绝对必要的,但这只是一个好主意。

第三,我认为“30 个字符”主要与他们示例的上下文相关,他们使用 rand() 泵入 md5()。鉴于 md5() 产生十六进制值,那么每个字符只有 16 个可能的值。此外,md5 摘要的长度为 32 个字符,但如果您开始截断它,那么发现冲突会容易得多。

简而言之:

  1. 为应用程序开发人员建议高熵而不是复杂的方法来确保值安全可靠地过期更容易。
  2. 如果人们使用他们的示例代码(即 md5(rand()))并开始截断 32 位值,那么攻击者可能只需要找到值 0 到 32767 的 md5 哈希的所有不同的 n 个字符前缀,其中 n是您截断的内容。这可能会产生一组值,这些值可以在可行的时间内被暴力破解。

查看该页面上的示例代码:

  • PHP 代码使用rand(),在某些系统上最多为15 位,甚至可能更少,因为rand()不一定是好的 RNG。
  • Python 代码使用 36 个字母表中的 32 个字符(大写和数字),计算结果约为。165 位假设这random.choice是完美的。它可能不是,但我们仍然在寻找超过 15 位的数据。
  • Java 代码中 130 位的使用介于两者之间。我看不出有什么明显的动机:这个数字是以 32 为基数输出的,所以要遵循“大约 30 个字符”的建议,我希望它是 150 位而不是 130 位。可以想象,这可能是一个错字!
  • 在所呈现的不同示例中,不变的是字符串的长度“大约 30 个字符”(Java 为 26,其他为 32)。这条始终如一的建议并没有告诉您太多关于安全性的信息,只是关于存储、传输和接收它的便利性。

我怀疑这个问题是否存在明确的答案,除了找出 Java 片段的作者是如何特别选择数字 130 的。如果这个熵的数量对 Google 的安全建议真的很重要,那么 Python 和 PHP 示例也会使用它。他们没有。

工作的一般原则是生成、存储和传输这些数据量的成本很低。因此,仅使用您真正需要的量没有任何好处,也存在一定的风险。结果是建议“矫枉过正”,尽管 PHP 代码不足。

你问为什么不是 1024 位——嗯,base-64 编码需要 172 个字符,也许你会争辩说这确实开始在网页的重量上接近显着,尽管这会是一个延伸。我不知道你不能使用这么大的 CSRF 令牌的任何原因,只是不值得建议人们使用它们。在 130 和 1024 之间进行选择是(根据经验)舒适过度杀伤和暴饮暴食之间差异的问题。在 130 和 64 之间进行选择可能更像是舒适的过度杀伤和唠叨的感觉之间的区别,也许你在一个或两个数量级之内,毕竟对蛮力并不安全。

因此,在就此类选择提供一般建议时,分析最坏情况,将答案加倍(或更多),然后检查结果是否在合理的资源限制范围内,这是相当合理的。如果是这样,只需使用它。因此,您不会期望数字 30 或 130 有什么特别之处。它可能对 Google 展示其工作具有指导意义。

关键是要使蛮力攻击不可行,而不仅仅是困难。

是的,在某些假设下,您可以使令牌的长度刚好足以匹配一些任意选择的概率。但你为什么要这样做?为什么要以用户和应用程序的安全性为赌注?这不像一堆伪随机位是一个巨大的成本因素。

当人们根本不想担心暴力攻击(或意外碰撞)时,像 128 位这样的东西是一个非常常见的选择。您可以在许多不同的应用程序中找到这个“神奇数字”,例如对称密钥、哈希盐、UUID 等。