您真的需要安全的随机 UUID 吗?
Do you really need secure random UUIDs?
我不是第一次遇到这个问题,大量使用 UUID 的项目会产生性能影响,有时需要几秒钟才能生成 UUID。
我知道为什么会这样。这个问题不是关于如何修复它,那里有很多信息。我更好奇它是否真的如此普遍以至于它应该成为默认值...
Java 防御性地在生成 UUID.randomUUID()
时默认使用 SecureRandom,但是大多数人使用 UUID 作为他们放入数据库的不同事物的 ID,例如用户、交易、订单 ID ,或任何其他实体 ID...但这些在安全方面并不敏感(或者我错了吗?),如果你是一名黑客并且你想出了如何猜测这些 ID 它不会给你任何优势。如果有人生成一个 uuid 并对其进行 MD5 并将其存储为新帐户的默认密码,这很重要......非常罕见的情况,但很明显它是敏感数据,真正的随机性在这里很重要。
我在这里的论点是,99% 的 UUID.randomUUID() 使用实际上并不关心真正的随机性,使用安全随机不是他们想要的行为。我在这里想念什么吗?我不是黑客,我曾遇到过一些看似无害但最终在安全方面意义重大的情况。
所以我的问题是:假设人们不对敏感数据(例如私有 key/password 生成)使用生成的 UUID,实体 ID 的安全随机性真的很重要吗?这在 UUID 生成中真正重要的情况是什么?
随机 (v4) UUID 必须提供大约 128 位的随机性。为了正确地做到这一点,需要使用至少有 128 位熵的 PRNG,这样输出就不会最终相关或具有阻止它们独立的模式。出于同样的原因,该 PRNG 也不得公开其内部状态,这意味着它几乎肯定需要是 CSPRNG。
此外,UUID 需要是普遍唯一的:这就是它们被称为普遍唯一标识符的原因。如果它们基于弱 PRNG,例如具有 32 位种子的 PRNG,尤其是当该种子基于像时间这样简单的东西时,不同的系统可能会生成相同的 UUID。如果您同时部署了大量系统,并且所有系统都大约在同一时间启动了基于时间的弱 PRNG,则情况尤其如此。
此外,还有速度极快的CSPRNG。 ChaCha20 是 Linux 内核中使用的 PRNG,每个内核可以输出超过 3 GB/s。 Rust 使用 ChaCha12 作为其默认 PRNG,它在密码学上也是安全的并且速度更快,并且可以配置为使用每线程选项。我不知道 Java 的默认选项是什么,但它本质上没有理由需要很慢,而且由于有许多快速、安全的选项,所以当你不这样做时,没有理由使用弱选项不得不。
最后,使用安全默认值是有意义的。如果我们让人们在快速但非独特和较慢但独特之间做出选择,许多人会选择错误并最终导致损坏或安全问题,因为它有点快。我们不提供这些选项,因为正如我所提到的,提供可能导致损坏或安全漏洞的选项是不明智的,而这些选项几乎不需要它们并且有很多缺点。
我不是第一次遇到这个问题,大量使用 UUID 的项目会产生性能影响,有时需要几秒钟才能生成 UUID。
我知道为什么会这样。这个问题不是关于如何修复它,那里有很多信息。我更好奇它是否真的如此普遍以至于它应该成为默认值...
Java 防御性地在生成 UUID.randomUUID()
时默认使用 SecureRandom,但是大多数人使用 UUID 作为他们放入数据库的不同事物的 ID,例如用户、交易、订单 ID ,或任何其他实体 ID...但这些在安全方面并不敏感(或者我错了吗?),如果你是一名黑客并且你想出了如何猜测这些 ID 它不会给你任何优势。如果有人生成一个 uuid 并对其进行 MD5 并将其存储为新帐户的默认密码,这很重要......非常罕见的情况,但很明显它是敏感数据,真正的随机性在这里很重要。
我在这里的论点是,99% 的 UUID.randomUUID() 使用实际上并不关心真正的随机性,使用安全随机不是他们想要的行为。我在这里想念什么吗?我不是黑客,我曾遇到过一些看似无害但最终在安全方面意义重大的情况。
所以我的问题是:假设人们不对敏感数据(例如私有 key/password 生成)使用生成的 UUID,实体 ID 的安全随机性真的很重要吗?这在 UUID 生成中真正重要的情况是什么?
随机 (v4) UUID 必须提供大约 128 位的随机性。为了正确地做到这一点,需要使用至少有 128 位熵的 PRNG,这样输出就不会最终相关或具有阻止它们独立的模式。出于同样的原因,该 PRNG 也不得公开其内部状态,这意味着它几乎肯定需要是 CSPRNG。
此外,UUID 需要是普遍唯一的:这就是它们被称为普遍唯一标识符的原因。如果它们基于弱 PRNG,例如具有 32 位种子的 PRNG,尤其是当该种子基于像时间这样简单的东西时,不同的系统可能会生成相同的 UUID。如果您同时部署了大量系统,并且所有系统都大约在同一时间启动了基于时间的弱 PRNG,则情况尤其如此。
此外,还有速度极快的CSPRNG。 ChaCha20 是 Linux 内核中使用的 PRNG,每个内核可以输出超过 3 GB/s。 Rust 使用 ChaCha12 作为其默认 PRNG,它在密码学上也是安全的并且速度更快,并且可以配置为使用每线程选项。我不知道 Java 的默认选项是什么,但它本质上没有理由需要很慢,而且由于有许多快速、安全的选项,所以当你不这样做时,没有理由使用弱选项不得不。
最后,使用安全默认值是有意义的。如果我们让人们在快速但非独特和较慢但独特之间做出选择,许多人会选择错误并最终导致损坏或安全问题,因为它有点快。我们不提供这些选项,因为正如我所提到的,提供可能导致损坏或安全漏洞的选项是不明智的,而这些选项几乎不需要它们并且有很多缺点。