通常生成随机标识符以通过 API 公开,而不是使用简单的自动递增主键。原因有很多:
- 防止简单的枚举。
- 不放弃创建的订单对象。
- 不放弃对象总数或增长率。
在生成这些 ID 时,使用加密安全的伪随机数生成器(如crypto.randomBytes()在 Node.js 中)与使用更简单的东西(如在 中Math.random())相比,是否有任何实质性的好处?
通常生成随机标识符以通过 API 公开,而不是使用简单的自动递增主键。原因有很多:
在生成这些 ID 时,使用加密安全的伪随机数生成器(如crypto.randomBytes()在 Node.js 中)与使用更简单的东西(如在 中Math.random())相比,是否有任何实质性的好处?
Math.random()通常从一天中的当前时间播种。因此,每天大约在同一时间生成的对象有可能发生碰撞。
从安全角度来看,这意味着攻击者可以预测这些“随机”值。
即使用其他东西播种,如果捕获了足够的值,非密码安全的伪随机数生成器也可以揭示其状态。这就是为什么从安全的角度来看,最好只使用由密码安全的伪随机数生成器(例如crypto.randomBytes()您所指的)生成的值。
CSPRNG具有某些特性,使其适用于安全性:
- 每个 CSPRNG 都应该满足下一位测试。也就是说,给定随机序列的前 k 位,没有多项式时间算法可以以优于 50% 的成功概率预测第 (k+1) 位。Andrew Yao 在 1982 年证明,通过下一位测试的生成器将通过所有其他多项式时间统计测试的随机性。
- 每个 CSPRNG 都应该承受“状态妥协扩展”。如果它的部分或全部状态已被揭示(或被正确猜测),那么在揭示之前重建随机数流应该是不可能的。此外,如果在运行时存在熵输入,则使用输入状态的知识来预测 CSPRNG 状态的未来条件应该是不可行的。
使用这样的生成器将满足您的安全要求:
但是,您不应该让这些现在不可预测的事实无法通过适当的访问控制(身份验证和授权)来保护第一个案例。URL 不应被视为秘密,它们可能会通过多种方式泄漏(使用隐藏摄像头进行肩冲浪、HTTP 引用标头泄漏、代理和浏览器日志)。此外,一旦一个 URL 已知并且您依靠不可预测的秘密来保护它,访问它的用户将始终具有访问权限,并且这不能被撤销。记住——他们可以很容易地为它添加书签。
如果您的应用程序认为有必要,使用不可预测的值肯定会对您的其他要求有所帮助。我建议使用 128 位随机值,该值足以防止可预测性和冲突。
是的。要完成您列出的所有事情,需要一个加密安全的 RNG。
我不知道 Math.random 是如何工作的,但想想一个可怕的 RNG,我们将其称为 2bitRNG,它仅以 2 位为种子,但产生 128 位数字。我用我的 2 位熵播种 2bitRNG,然后用它产生 10,000 个随机数。
因为我只用 2 位播种,所以只有 4 种可能的种子。因此,攻击者只需搜索 40,000 个可能的随机数空间即可找到正确的种子,并在您当前所在的序列中定位。这是一个很小的数字,很可能可以用一台普通的计算机在几分之一秒内计算出来。一旦攻击者知道这一点,他就知道下一个序列,以及你产生了多少个 RNG。
我不知道 Math.random 有多糟糕或者它可能容易受到哪些其他攻击,但一般建议是不要将它用于安全目的,因为它不安全。适当的安全 RNG 使用大量熵(约 128 位或更高)来防止我刚刚描述的那种攻击,并且不会产生可预测的结果。
退后一步,从不同的角度看待你的问题。如果您使用序列号,可能发生的最糟糕的事情是什么?您提到了枚举:枚举的威胁是什么?如果有人看到您发出 #00015 和 #00016 并预测接下来可能会生成 ID #00017,他们会导致问题吗?也许有人可以劫持可能与即将到来的 ID 相关联的电子邮件或 Twitter 帐户。
如果有人可以确定 ID #00023 比 ID #42234 旧,那么问题是什么?旧 ID 通常比新 ID 有更多可用资源吗?或许我知道ID#00123是5月1日发的,ID#00256是6月1日发的;如果我得知某位当红名人宣布他们在 5 月注册,我只有 133 个号码可以猜出他们的 ID。猜测某人的 ID# 是否会给任何人带来问题,还是无论如何都会成为公共信息?
此时,您应该了解顺序 ID #s 是否真的必须保密,或者它们是否可以公开。
然后查看威胁向量(如果这些是现实威胁)并提出风险。如果您的客户的秘密 ID 公开,其中大多数似乎对您的客户构成风险,并且对您的系统的威胁较小。但是,如果您不保护您的客户,您的声誉肯定会被破坏,您的企业也会失败。因此,如果这些是有效的威胁,您必须认真对待它们。
接下来,补充一点,伪随机数生成器 (PRNG) 的创建正是为了一个特定目的:生成统计上分布良好的数字,用于建模统计问题。它们从来没有被创建来生成“不可猜测”的数字。大多数 PRNG 只是一个数学问题,而不是序列号生成器。您可以尝试跳过许多播种圈以减少生成可猜测数字的能力,但这正是密码安全 PRNG (CSPRNG) 旨在解决的问题。CSPRNG 专门设计用于生成无法猜测的数字。
既然您更好地了解了一些风险,您应该明白为什么需要(或不需要)保护您的客户免受这些威胁;通过更好地了解 PRNG 和 CSPRNG 之间的差异,您可以做出可靠的决定。
如果您必须保守这些秘密,那么您必须使用 CSPRNG 来保护您的客户。如果您不需要对它们保密,那么您应该使用序列号并通过完全消除随机数生成器来降低系统的复杂性。
tl; dr:这取决于,如果可以的话,使用 CSPRNG。
虽然经常使用不是(一定是)来自 CSPRNG 的 UUID,但如果(不是 CS)-PRNG 没有定期重新播种(如在长期存在的服务器进程中),这可能不是一个好主意
如果您用于创建此类标识符的算法是已知的,并且可以使用几个样本来估计它的输入(例如 C 的情况rand()),那么您会丢失您在问题中列举的所有这些属性。
虽然使用可预测的 PRNG 从单个种子生成值是很糟糕的,但如果在每次使用之前以良好的熵重新播种坏的 PRNG(因为这是大多数 Web 场景中的常规情况),那应该没问题。
因此,如果可以,请使用 CSPRNG。如果不能,请尝试在每个生成步骤之前用良好的熵重新播种。
这一切都取决于您的生成过程是如何播种和使用的。
如果你有一个长时间运行的进程,让它使用 CSPRNG。如果您使用的是短期过程,请使用良好的熵源和基本上任何 PRNG。