ChatGPT解决这个技术问题 Extra ChatGPT

UUID 有多独特?

使用 UUID 来唯一标识某物有多安全(我将它用于上传到服务器的文件)?据我了解,它基于随机数。然而,在我看来,如果有足够的时间,它最终会自我重复,只是纯属偶然。是否有更好的系统或某种类型的模式来缓解这个问题?

对于“足够的时间”的足够大的价值:)
“UUID 有多独特?”普遍独一无二,我相信。 ;)
除非您计划在 Venus 上进行开发,否则 GUID 就足够了。
“独特”意味着永远不会发生碰撞。如果它有任何碰撞的可能性,它就不是唯一的。因此,根据定义,UUID 不是唯一的,并且只有在您准备好应对潜在碰撞时才安全,而不管发生碰撞的可能性如何。否则,您的程序就是不正确的。您可以说 UUID “几乎是唯一的”,但这并不意味着它是“唯一的”。
UUID 是唯一的“出于实际目的” - 事实上,生成重复值的可能性非常小,这并不会使依赖于这个的程序不正确,除非在非常罕见的情况下,生成的 ID 的数量开始使可能性具有统计学意义。

M
Martijn Pieters

非常安全:

一个人每年被陨石击中的风险估计为 170 亿分之一,这意味着概率约为 0.00000000006 (6 × 10−11),相当于创建几十万亿个 UUID 的概率在一年内,并且有一个重复。换句话说,仅在接下来的 100 年每秒生成 10 亿个 UUID 之后,仅创建一个副本的概率约为 50%。

警告:

但是,这些概率仅在使用足够的熵生成 UUID 时才成立。否则,重复的概率可能会显着提高,因为统计离散度可能会更低。在分布式应用程序需要唯一标识符的情况下,即使合并来自多个设备的数据,UUID 也不会发生冲突,每个设备上使用的种子和生成器的随机性在应用程序的生命周期内必须是可靠的。如果这不可行,RFC4122 建议使用命名空间变体。

资料来源:关于通用唯一标识符的 Wikipedia 文章的Random UUID probability of duplicates section(链接指向 2016 年 12 月的修订版,然后编辑重新修改了该部分)。

另请参阅同一通用唯一标识符文章 Collisions 中有关同一主题的当前部分。


我喜欢 Wikipedia 中的这一部分:但是,这些概率仅在使用足够的熵生成 UUID 时才成立。否则,重复的概率可能会显着提高,因为统计离散度可能会更低。那么重复注意到这句话的真正机会是多少。我们不能在计算机上创建真正的随机数,可以吗?
实际上,已经做了大量工作来寻找将尽可能多的熵(“真正的随机性”,我猜你会这么称呼它)引入随机数 API 的方法。请参阅en.wikipedia.org/wiki/Entropy_%28computing%29
这实际上是比我想象的更高的碰撞概率。生日悖论,我猜。
如何使用“足够的熵”在 Node.js 中生成 UUID?
@linus_hologram - 这不是一个真正的 JS 问题。熵源通常在操作系统级别进行配置。
r
rein

如果“给定足够的时间”是指 100 年,并且你以每秒 10 亿的速度创造它们,那么是的,100 年后你有 50% 的机会发生碰撞。


但只有在这些 ID 使用了 256 EB 的存储空间之后。
有趣的是,你可以连续生成 2 个相同的,当然,在巧合、运气和神圣干预的水平上令人难以置信,但尽管几率高不可测,它仍然是可能的! :D 是的,它不会发生。只是为了想一想您创建副本的那一刻而说的有趣!截图视频!
唯一性纯粹是因为随机性吗?还是有其他因素? (例如时间戳、IP 等)
@TheTahaan 这不是随机的意思。这并不意味着“完全不可预测”——通常它们遵循某种分布。如果你掷 10 个硬币,得到 2 个正面,然后是 3 个反面,然后是 5 个正面的机会非常低(2^-10,大约 0.001)。这确实是随机的,但我们绝对可以知道获得特定结果的机会。我们只是不能提前说它是否会发生。
只是为了解释这个实现做错了什么,他们使用的是版本 1 UUID,它依赖于时间戳和 mac 地址的组合来实现其唯一性。但是,如果您生成 UUID 的速度足够快,则时间戳还不会增加。在这种情况下,您的 UUID 生成算法应该跟踪使用的最后一个时间戳并将其增加 1。他们显然没有采取这一步。但是,同一台机器在短时间内正确生成的所有版本 1 UUID 都会表现出明显的相似性,但应该仍然是唯一的。
H
Hoylen

UUID 的类型不止一种,因此“安全程度”取决于您使用的类型(UUID 规范称为“版本”)。

版本 1 是基于时间的加上 MAC 地址的 UUID。 128 位包含 48 位网卡 MAC 地址(由制造商唯一分配)和 60 位时钟,分辨率为 100 纳秒。该时钟在公元 3603 年结束,因此这些 UUID 至少在此之前是安全的(除非您每秒需要超过 1000 万个新 UUID,或者有人克隆了您的网卡)。我说“至少”是因为时钟从 1582 年 10 月 15 日开始,所以在时钟结束后大约有 400 年的时间,甚至还有很小的重复可能性。

版本 4 是随机数 UUID。有六个固定位,其余的 UUID 是 122 位随机性。请参阅 Wikipedia 或其他分析,这些分析描述了重复的可能性很小。

版本 3 使用 MD5,版本 5 使用 SHA-1 来创建那些 122 位,而不是随机或伪随机数生成器。因此,就安全性而言,版本 4 就像是一个统计问题(只要您确保摘要算法正在处理的内容始终是唯一的)。

版本 2 与版本 1 类似,但时钟更小,因此它会更快地环绕。但由于第 2 版 UUID 用于 DCE,因此您不应该使用它们。

因此,对于所有实际问题,它们都是安全的。如果您不愿意将其留给概率(例如,您是那种担心地球会在您的一生中被大型小行星摧毁的人),请确保您使用版本 1 UUID,并且它保证是唯一的(在你有生之年,除非你计划活过公元 3603 年)。

那么为什么不是每个人都简单地使用版本 1 UUID?这是因为第 1 版 UUID 揭示了生成它的机器的 MAC 地址,并且它们是可预测的——这两件事可能对使用这些 UUID 的应用程序有安全影响。


当许多人使用同一服务器生成时,默认使用版本 1 UUID 会出现严重问题。第 4 版 UUID 是我的默认设置,因为您可以快速编写一些内容以使用任何语言或平台(包括 javascript)生成一个。
@Hoylen 解释得很好!但是需要这么夸张吗?
理论上,它是由制造商唯一分配的。
无需在一秒钟内生成 1000 万个版本 1 UUID 即可遇到重复;为了溢出序列号,只需在单个“滴答”的范围内生成一批 16,384 个 UUID。我已经看到这种情况发生在一个天真地依赖时钟源的实现中,该时钟源 (1) 具有 μs 级粒度,并且 (2) 不能保证是单调的(系统时钟不是)。小心你使用的 UUID 生成代码,尤其要小心基于时间的 UUID 生成器。它们很难正确处理,因此在使用它们之前对它们进行负载测试。
h
hookenz

这个问题的答案可能很大程度上取决于 UUID 版本。

许多 UUID 生成器使用版本 4 随机数。但是,其中许多使用伪随机数生成器来生成它们。

如果使用带有小周期的不良种子 PRNG 来生成 UUID,我会说它根本不是很安全。一些随机数生成器的方差也很差。即比其他人更频繁地偏爱某些数字。这不会很好地工作。

因此,它仅与用于生成它的算法一样安全。

另一方面,如果您知道这些问题的答案,那么我认为版本 4 的 uuid 使用起来应该非常安全。事实上,我正在使用它来识别网络块文件系统上的块,到目前为止还没有发生冲突。

在我的例子中,我使用的 PRNG 是一个 mersenne twister,我很小心它的播种方式,它来自多个来源,包括 /dev/urandom。 Mersenne twister 的周期为 2^19937 − 1。在我看到重复的 uuid 之前需要很长时间。

所以选择一个好的库或者自己生成它,并确保你使用了一个像样的 PRNG 算法。


S
Stephen C

我同意其他答案。 UUID 对于几乎所有实际用途1 都足够安全,当然对于您的用途也是如此。

但是假设(假设地)他们不是。

是否有更好的系统或某种类型的模式来缓解这个问题?

这里有几种方法:

使用更大的 UUID。例如,不要使用 128 个随机位,而是使用 256 或 512 或......您添加到类型 4 样式 UUID 的每个位都会将冲突的概率降低一半,假设您有一个可靠的熵源2。构建一个集中式或分布式服务,生成 UUID 并记录它曾经发布的每一个。每次生成一个新的 UUID 时,它都会检查该 UUID 是否以前从未发布过。如果我们假设运行该服务的人绝对值得信赖、廉洁奉公等等,那么这样的服务在技术上就可以直接实施(我认为)。不幸的是,它们不是……尤其是当政府的安全组织有可能进行干预时。因此,这种方法可能是不切实际的,并且在现实世界中可能是不可能的。

- 如果UUID的唯一性决定了核导弹是否在您的国家首都发射,您的许多同胞不会相信“概率极低”。因此我的“几乎所有”资格。 2 - 这是一个哲学问题。有什么东西真的是随机的吗?如果不是,我们怎么知道?我们所知道的宇宙是模拟的吗?有没有可以想象的“调整”物理定律来改变结果的上帝? 3 - 如果有人知道有关此问题的任何研究论文,请发表评论。


我只想指出,方法 2 基本上违背了使用 UUID 的主要目的,此时您不妨只使用经典的编号 ID。
我不同意。顺序编号 ID 的缺陷在于它们太容易被猜到。您应该能够以使 UUID 难以猜测的方式实现方法 2。
但即使你所说的你基本上可以使用任何其他随机字符串/数字并检查重复项,你没有任何理由使用 UUID 而不是说 6 个字符长的随机字符串。
嗯,是的,也不是。这取决于要求 id 唯一的上下文。如果只要求它们在封闭系统中是唯一的,那么使用短随机字符串并将它们全部存储在数据库(或其他东西)中以检查重复项是可行的。但这并不能保证您具有普遍的独特性。如果在系统的生命周期内生成的唯一 ID 的数量足够大,那么您将遇到扩展问题,假设唯一 ID 随着时间的推移必须是唯一的……而不仅仅是在某个时间点。
为什么使用集中式数据库不能保证通用唯一性?这对我来说没有任何意义。
D
Dave Vogt

引自 Wikipedia

因此,任何人都可以创建一个 UUID 并使用它来识别某些东西,并且有合理的信心,该标识符永远不会被任何人无意中用于其他任何事情

它继续非常详细地解释了它实际上是多么安全。所以回答你的问题:是的,它足够安全。


l
lost

对于 UUID4,我认为在一个边长为 360,000 公里的立方体形盒子中,ID 的数量大约与沙粒的数量一样多。这是一个边长约 2 1/2 倍于木星直径的盒子。

工作以便有人可以告诉我我是否搞砸了单位:

沙粒体积 0.00947mm^3 (Guardian)

UUID4 有 122 个随机位 -> 5.3e36 个可能值(维基百科)

那么多沙粒的体积 = 5.0191e34 mm^3 或 5.0191e+25m^3

具有该体积的立方体的边长 = 3.69E8m 或 369,000km

木星直径:139,820 公里(谷歌)


实际上我想这假设 100% 包装所以也许我应该为此添加一个因素!
这实际上非常有帮助,让我意识到这可能没问题,还有其他事情需要担心。囧
假设盒子里装满了沙子。您需要指定所有假设
显然它是一个完整的盒子,否则我可以说“一个已知宇宙大小的盒子,它是 0.0000000002% 的完整盒子”(例如,没有计算出来!),描述性较差。我认为包装因素是上述计算的一个更大的问题,但至少它是保守的(即比 100% 更现实的值会使盒子更大)。
P
Parappa

UUID 方案通常不仅使用伪随机元素,还使用当前系统时间,以及某种通常唯一的硬件 ID(如果可用),例如网络 MAC 地址。

使用 UUID 的全部意义在于,您相信它在提供唯一 ID 方面比您自己能够做得更好。这与使用 3rd 方密码库而不是滚动自己的密码库背后的理由相同。自己做可能会更有趣,但这样做通常不太负责任。


P
Posthuma

多年来一直这样做。永远不要遇到问题。

我通常将我的数据库设置为有一个包含所有键和修改日期等的表。从来没有遇到过重复键的问题。

它的唯一缺点是,当您编写一些查询以快速查找某些信息时,您需要对密钥进行大量复制和粘贴。您不再拥有简短易记的 ID。


T
Tschallacka

这是一个测试片段,供您测试它的独特性。受@scalabl3 评论的启发

有趣的是,你可以连续生成 2 个相同的,当然,在巧合、运气和神圣干预的水平上令人难以置信,但尽管几率高不可测,它仍然是可能的! :D 是的,它不会发生。只是为了想一想您创建副本的那一刻而说的有趣!截图视频! – scalabl3 2015 年 10 月 20 日 19:11

如果您觉得幸运,请选中复选框,它只检查当前生成的 id。如果您希望进行历史检查,请不要选中它。请注意,如果您不选中它,您可能会在某个时候用完 ram。我试图让它对 cpu 友好,以便您可以在需要时快速中止,只需再次点击运行片段按钮或离开页面。

Math.log2 = Math.log2 || function(n){ return Math.log(n) / Math.log(2); } Math.trueRandom = (function() { var crypt = window.crypto || window.msCrypto; if (crypt && crypt.getRandomValues) { // 如果我们有一个加密库,使用它 var random = function(min, max ) { var rval = 0; var range = max - min; if (range < 2) { return min; } var bits_needed = Math.ceil(Math.log2(range)); if (bits_needed > 53) { throw new Exception ("我们不能生成大于 53 位的数字。"); } var bytes_needed = Math.ceil(bits_needed / 8); var mask = Math.pow(2, bits_needed) - 1; // 7776 -> (2^13 = 8192) -1 == 8191 或 0x00001111 11111111 // 创建字节数组并填充 N 个随机数 var byteArray = new Uint8Array(bytes_needed); crypt.getRandomValues(byteArray); var p = (bytes_needed - 1) * 8; for (var i = 0; i < bytes_needed; i++ ) { rval += byteArray[i] * Math.pow(2, p); p -= 8; } // 使用 & 应用掩码,减少递归次数查找 rval = rval & mask; if (rval >= range) { // 超出可接受范围的整数 return random(min, max); } // 返回一个整数 t hat 落在返回 min + rval 的范围内; } return function() { var r = random(0, 1000000000) / 1000000000;返回 r; }; } else { // 来自 http://baagoe.com/en/RandomMusings/javascript/ // Johannes Baagøe , 2010 function Mash() { var n = 0xefc8249d; var mash = function(data) { data = data.toString(); for (var i = 0; i < data.length; i++) { n += data.charCodeAt(i);变量 h = 0.02519603282416938 * n; n = h >>> 0; h -= n; h *= n; n = h >>> 0; h -= n; n += h * 0x100000000; // 2^32 } return (n >>> 0) * 2.3283064365386963e-10; // 2^-32 }; mash.version = '混搭 0.9';返回捣碎; } // 来自 http://baagoe.com/en/RandomMusings/javascript/ function Alea() { return (function(args) { // Johannes Baagøe , 2010 var s0 = 0; var s1 = 0; var s2 = 0; var c = 1; if (args.length == 0) { args = [+new Date()]; } var mash = Mash(); s0 = mash(' ') ; s1 = mash(' '); s2 = mash(' '); for (var i = 0; i < args.length; i++) { s0 -= mash(args[i]); if (s0 < 0) { s0 += 1; } s1 -= mash(args[i]); if (s1 < 0) { s1 += 1; } s2 -= mash(args[i]); if (s2 < 0) { s2 += 1; } } mash = null; var random = function() { var t = 2091639 * s0 + c * 2.3283064365386963e-10; // 2^-32 s0 = s1; s1 = s2; return s2 = t - (c = t | 0); }; random.uint32 = function() { return random() * 0x100000000; // 2^32 }; random.fract53 = function() { return random() + (random() * 0x200000 | 0) * 1.1102230246251565e-16; // 2^-53 }; random.version = 'Alea 0.9'; random.args = args; return random; }(Array.prototype.slice.call(arguments))) ; };返回阿莱亚(); } }()); Math.guid = function() { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { var r = Math.trueRandom() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8); return v.toString(16); }); }; function logit(item1, item2) { console.log("Do "+item1+" 和 "+item2+" 相等吗?"+(item1 == item2 ? "OMG!截图,你会成为密码学世界的史诗,现在买彩票!":"不,他们没有。羞耻。没有名气")+ ", runs: "+window.numberofRuns); } 运行次数 = 0;函数 test() { window.numberofRuns++; var x = Math.guid(); var y = Math.guid(); var 测试 = x == y ||历史测试(x,y);对数(x,y);返回测试; } historyArr = [];历史计数 = 0;函数historyTest(item1, item2) { if(window.luckyDog) { return false; } for(var i = historyCount; i > -1; i--) { logit(item1,window.historyArr[i]); if(item1 == history[i]) { return true; } logit(item2,window.historyArr[i]); if(item2 == history[i]) { return true; } } window.historyArr.push(item1); window.historyArr.push(item2); window.historyCount+=2;返回假; } 幸运狗 = 假; document.body.onload = function() { document.getElementById('runit').onclick = function() { window.luckyDog = document.getElementById('lucky').checked; var val = document.getElementById('input').value if(val.trim() == '0') { var intervaltimer = window.setInterval(function() { var test = window.test(); if(test ) { window.clearInterval(intervaltimer); } },0); } else { var num = parseInt(val); if(num > 0) { var intervaltimer = window.setInterval(function() { var test = window.test(); num--; if(num < 0 || test) { window.clearInterval(intervaltimer); } } ,0); } } }; };请输入计算运行的频率。永远设置为 0。如果您觉得幸运,请选中复选框。


尝试使用 RFC 4122 版本 1(日期时间和 MAC 地址)UUID。
T
TheTechRobo Stands for Ukraine

我不知道这对您是否重要,但请记住 GUIDs are globally unique, but substrings of GUIDs aren't


请记住,此处链接的参考资料讨论的是版本 1 UUID(它将有关生成计算机等的信息带入 id)。大多数其他答案都在谈论第 4 版(完全随机)。上面链接的 Wikipedia 文章 en.wikipedia.org/wiki/Universally_unique_identifier 解释了不同种类的 UUID。
T
Toby Kelsey

我应该提到我在亚马逊上购买了两个外部希捷驱动器,它们具有相同的设备 UUID,但不同的 PARTUUID。据推测,他们用来格式化驱动器的克隆软件也只是复制了 UUID。

显然,由于克隆或复制过程有缺陷而不是随机巧合,UUID 冲突更有可能发生。在计算 UUID 风险时请记住这一点。