用于 URL 上的 id 段的 Java UUID.randomUUID() 或 SecureRandom?

信息安全 爪哇 随机的 休息
2021-09-10 03:59:35

在构建需要 URL 上的唯一标识符的基于 Java 的系统时,是更好的选择UUID.randomUUID()还是SecureRandom更好的选择?

更具体地说,调用POST /items将返回一个201 Created带有 Location 的新创建的/items/{id}where{id}是一个随机字符串,并且该 URL 旨在供匿名 HTTP 请求在有限的时间窗口内使用。

B-KEPLFdWNZ4JTUnnEq3Og此代码返回一个包含 128 位随机性的 22 个字符的字符串(例如)。

SecureRandom random = new SecureRandom();
byte bytes[] = new byte[16];
random.nextBytes(bytes);
Encoder encoder = Base64.getUrlEncoder().withoutPadding();
String token = encoder.encodeToString(bytes);
System.out.println(token);

UUID.randomUUID()是“用于检索类型 4(伪随机生成)UUID 的静态工厂。UUID 是使用加密强伪随机数生成器生成的。” 此代码返回一个 36 个字符的字符串(例如f0803ce7-8e3d-421a-8126-e5a0bd0a865f),具有 122 位随机性。

UUID randomUUID = UUID.randomUUID();
System.out.println(randomUUID);

这些标识符也将作为固定长度CHAR类型保存在关系数据库中。

2个回答

就随机比特的原始数量而言,是的。

查看 Java 随机 UUID生成的源代码,您可以看到它们实际上利用SecureRandom该类来生成原始字节。所以,你的随机性的“质量”不会改变。

从技术上讲,您还有 6 位的事实确实使暴力复制变得更加困难。 Wikipedia 关于 UUID 的文章很好地描述了它背后的数学原理。引用它:

[A] 在接下来的 100 年每秒生成 10 亿个 UUID 后,仅创建一个副本的概率约为 50%

如果这些链接将在 24 小时后过期,这只是一件需要担心的小事,而且只有 122 位。

现在,更多的位是否会使攻击者更难暴力碰撞?当然!这值得么?也许……但也许不是。只有您才能真正决定什么适合您的用例。

当你达到蛮力碰撞平均需要比估计的宇宙热寂时间更长的地步时,它可能有点过火了。这就涉及到需要更多地担心黑客会损害您的存储安全,而不是暴力破解(老实说,即使使用 UUID,您也会遇到这个问题)。最终,如果他们能找到另一种方式,暴力破解的难度并不重要。如果你真的担心,存储很便宜——存储你用 a 生成的 256 位字符串SecureRandom并称之为好,但要确保你有适当的其他一切的安全性,否则它是无用的。

嗯,原则上,UUID 和加密 RNG 承诺两种不同的东西:

  1. UUID 承诺重复的概率低
  2. 加密 RNG 向恶意对手承诺不可预测性

如果 #2 是一个问题,我会确保使用加密 RNG。例如,如果您担心可以在短时间内请求大量 UUID 的恶意攻击者可能会学习到足够的信息来预测提供给其他用户请求的信息。

碰巧的是,Java 的UUID.randomUUID()方法确实使用了加密 RNG,因此在这方面它应该是安全的。但随之而来的风险是,后来查看您的代码的开发人员可能不明白您的意图包括安全加密随机选择,而不仅仅是唯一性。例如,有人可能会使用其他语言或不使用加密 RNG 的 UUID 库重写该特定模块,并将您暴露在您打算排除的基于预测的攻击中。因此,如果加密安全确实是一个重要因素,我会尝试通过显式使用以一种显而易见的方式编写代码SecureRandom(安全关键代码需要非常清晰!)

如果我只关心唯一性而不是安全性,那么我会继续使用UUID.randomUUID().